]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp
Vendor import of lldb trunk r290819:
[FreeBSD/FreeBSD.git] / tools / debugserver / source / MacOSX / DarwinLog / DarwinLogCollector.cpp
1 //===-- DarwinLogCollector.cpp ----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "DarwinLogCollector.h"
11 #include "ActivityStreamSPI.h"
12
13 #include <dlfcn.h>
14
15 #include <cinttypes>
16 #include <mutex>
17 #include <vector>
18
19 #include "DNB.h"
20 #include "DNBLog.h"
21 #include "DarwinLogTypes.h"
22 #include "LogFilterChain.h"
23 #include "LogFilterExactMatch.h"
24 #include "LogFilterRegex.h"
25 #include "LogMessageOsLog.h"
26 #include "MachProcess.h"
27 #include "RNBContext.h"
28 #include "RNBDefs.h"
29 #include "RNBRemote.h"
30
31 // Use an anonymous namespace for variables and methods that have no
32 // reason to leak out through the interface.
33 namespace {
34 /// Specify max depth that the activity parent-child chain will search
35 /// back to get the full activity chain name.  If we do more than this,
36 /// we assume either we hit a loop or it's just too long.
37 static const size_t MAX_ACTIVITY_CHAIN_DEPTH = 10;
38
39 // Used to tap into and retrieve logs from target process.
40 // (Consumer of os_log).
41 static os_activity_stream_for_pid_t s_os_activity_stream_for_pid;
42 static os_activity_stream_resume_t s_os_activity_stream_resume;
43 static os_activity_stream_cancel_t s_os_activity_stream_cancel;
44 static os_log_copy_formatted_message_t s_os_log_copy_formatted_message;
45 static os_activity_stream_set_event_handler_t
46     s_os_activity_stream_set_event_handler;
47
48 bool LookupSPICalls() {
49   static std::once_flag s_once_flag;
50   static bool s_has_spi;
51
52   std::call_once(s_once_flag, [] {
53     s_os_activity_stream_for_pid = (os_activity_stream_for_pid_t)dlsym(
54         RTLD_DEFAULT, "os_activity_stream_for_pid");
55     s_os_activity_stream_resume = (os_activity_stream_resume_t)dlsym(
56         RTLD_DEFAULT, "os_activity_stream_resume");
57     s_os_activity_stream_cancel = (os_activity_stream_cancel_t)dlsym(
58         RTLD_DEFAULT, "os_activity_stream_cancel");
59     s_os_log_copy_formatted_message = (os_log_copy_formatted_message_t)dlsym(
60         RTLD_DEFAULT, "os_log_copy_formatted_message");
61     s_os_activity_stream_set_event_handler =
62         (os_activity_stream_set_event_handler_t)dlsym(
63             RTLD_DEFAULT, "os_activity_stream_set_event_handler");
64
65     // We'll indicate we're all set if every function entry point
66     // was found.
67     s_has_spi = (s_os_activity_stream_for_pid != nullptr) &&
68                 (s_os_activity_stream_resume != nullptr) &&
69                 (s_os_activity_stream_cancel != nullptr) &&
70                 (s_os_log_copy_formatted_message != nullptr) &&
71                 (s_os_activity_stream_set_event_handler != nullptr);
72     if (s_has_spi) {
73       DNBLogThreadedIf(LOG_DARWIN_LOG, "Found os_log SPI calls.");
74       // Tell LogMessageOsLog how to format messages when search
75       // criteria requires it.
76       LogMessageOsLog::SetFormatterFunction(s_os_log_copy_formatted_message);
77     } else {
78       DNBLogThreadedIf(LOG_DARWIN_LOG, "Failed to find os_log SPI "
79                                        "calls.");
80     }
81   });
82
83   return s_has_spi;
84 }
85
86 using Mutex = std::mutex;
87 static Mutex s_collector_mutex;
88 static std::vector<DarwinLogCollectorSP> s_collectors;
89
90 static void TrackCollector(const DarwinLogCollectorSP &collector_sp) {
91   std::lock_guard<Mutex> locker(s_collector_mutex);
92   if (std::find(s_collectors.begin(), s_collectors.end(), collector_sp) !=
93       s_collectors.end()) {
94     DNBLogThreadedIf(LOG_DARWIN_LOG,
95                      "attempted to add same collector multiple times");
96     return;
97   }
98   s_collectors.push_back(collector_sp);
99 }
100
101 static void StopTrackingCollector(const DarwinLogCollectorSP &collector_sp) {
102   std::lock_guard<Mutex> locker(s_collector_mutex);
103   s_collectors.erase(
104       std::remove(s_collectors.begin(), s_collectors.end(), collector_sp),
105       s_collectors.end());
106 }
107
108 static DarwinLogCollectorSP FindCollectorForProcess(pid_t pid) {
109   std::lock_guard<Mutex> locker(s_collector_mutex);
110   for (const auto &collector_sp : s_collectors) {
111     if (collector_sp && (collector_sp->GetProcessID() == pid))
112       return collector_sp;
113   }
114   return DarwinLogCollectorSP();
115 }
116
117 static FilterTarget TargetStringToEnum(const std::string &filter_target_name) {
118   if (filter_target_name == "activity")
119     return eFilterTargetActivity;
120   else if (filter_target_name == "activity-chain")
121     return eFilterTargetActivityChain;
122   else if (filter_target_name == "category")
123     return eFilterTargetCategory;
124   else if (filter_target_name == "message")
125     return eFilterTargetMessage;
126   else if (filter_target_name == "subsystem")
127     return eFilterTargetSubsystem;
128   else
129     return eFilterTargetInvalid;
130 }
131
132 class Configuration {
133 public:
134   Configuration(const JSONObject &config)
135       : m_is_valid(false),
136         m_activity_stream_flags(OS_ACTIVITY_STREAM_PROCESS_ONLY),
137         m_filter_chain_sp(nullptr) {
138     // Parse out activity stream flags
139     if (!ParseSourceFlags(config)) {
140       m_is_valid = false;
141       return;
142     }
143
144     // Parse filter rules
145     if (!ParseFilterRules(config)) {
146       m_is_valid = false;
147       return;
148     }
149
150     // Everything worked.
151     m_is_valid = true;
152   }
153
154   bool ParseSourceFlags(const JSONObject &config) {
155     // Get the source-flags dictionary.
156     auto source_flags_sp = config.GetObject("source-flags");
157     if (!source_flags_sp)
158       return false;
159     if (!JSONObject::classof(source_flags_sp.get()))
160       return false;
161
162     const JSONObject &source_flags =
163         *static_cast<JSONObject *>(source_flags_sp.get());
164
165     // Parse out the flags.
166     bool include_any_process = false;
167     bool include_callstacks = false;
168     bool include_info_level = false;
169     bool include_debug_level = false;
170     bool live_stream = false;
171
172     if (!source_flags.GetObjectAsBool("any-process", include_any_process)) {
173       DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'any-process' missing from "
174                                        "configuration.");
175       return false;
176     }
177     if (!source_flags.GetObjectAsBool("callstacks", include_callstacks)) {
178       // We currently suppress the availability of this on the lldb
179       // side.  We include here for devices when we enable in the
180       // future.
181       // DNBLogThreadedIf(LOG_DARWIN_LOG,
182       //                  "Source-flag 'callstacks' missing from "
183       //                  "configuration.");
184
185       // OK.  We just skip callstacks.
186       // return false;
187     }
188     if (!source_flags.GetObjectAsBool("info-level", include_info_level)) {
189       DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'info-level' missing from "
190                                        "configuration.");
191       return false;
192     }
193     if (!source_flags.GetObjectAsBool("debug-level", include_debug_level)) {
194       DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'debug-level' missing from "
195                                        "configuration.");
196       return false;
197     }
198     if (!source_flags.GetObjectAsBool("live-stream", live_stream)) {
199       DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'live-stream' missing from "
200                                        "configuration.");
201       return false;
202     }
203
204     // Setup the SPI flags based on this.
205     m_activity_stream_flags = 0;
206     if (!include_any_process)
207       m_activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY;
208     if (include_callstacks)
209       m_activity_stream_flags |= OS_ACTIVITY_STREAM_CALLSTACK;
210     if (include_info_level)
211       m_activity_stream_flags |= OS_ACTIVITY_STREAM_INFO;
212     if (include_debug_level)
213       m_activity_stream_flags |= OS_ACTIVITY_STREAM_DEBUG;
214     if (!live_stream)
215       m_activity_stream_flags |= OS_ACTIVITY_STREAM_BUFFERED;
216
217     DNBLogThreadedIf(LOG_DARWIN_LOG, "m_activity_stream_flags = 0x%03x",
218                      m_activity_stream_flags);
219
220     return true;
221   }
222
223   bool ParseFilterRules(const JSONObject &config) {
224     // Retrieve the default rule.
225     bool filter_default_accept = true;
226     if (!config.GetObjectAsBool("filter-fall-through-accepts",
227                                 filter_default_accept)) {
228       DNBLogThreadedIf(LOG_DARWIN_LOG, "Setting 'filter-fall-through-accepts' "
229                                        "missing from configuration.");
230       return false;
231     }
232     m_filter_chain_sp.reset(new LogFilterChain(filter_default_accept));
233     DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog no-match rule: %s.",
234                      filter_default_accept ? "accept" : "reject");
235
236     // If we don't have the filter-rules array, we're done.
237     auto filter_rules_sp = config.GetObject("filter-rules");
238     if (!filter_rules_sp) {
239       DNBLogThreadedIf(LOG_DARWIN_LOG,
240                        "No 'filter-rules' config element, all log "
241                        "entries will use the no-match action (%s).",
242                        filter_default_accept ? "accept" : "reject");
243       return true;
244     }
245     if (!JSONArray::classof(filter_rules_sp.get()))
246       return false;
247     const JSONArray &rules_config =
248         *static_cast<JSONArray *>(filter_rules_sp.get());
249
250     // Create the filters.
251     for (auto &rule_sp : rules_config.m_elements) {
252       if (!JSONObject::classof(rule_sp.get()))
253         return false;
254       const JSONObject &rule_config = *static_cast<JSONObject *>(rule_sp.get());
255
256       // Get whether this filter accepts or rejects.
257       bool filter_accepts = true;
258       if (!rule_config.GetObjectAsBool("accept", filter_accepts)) {
259         DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'accept' element missing.");
260         return false;
261       }
262
263       // Grab the target log field attribute for the match.
264       std::string target_attribute;
265       if (!rule_config.GetObjectAsString("attribute", target_attribute)) {
266         DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'attribute' element missing.");
267         return false;
268       }
269       auto target_enum = TargetStringToEnum(target_attribute);
270       if (target_enum == eFilterTargetInvalid) {
271         DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter attribute '%s' unsupported.",
272                          target_attribute.c_str());
273         return false;
274       }
275
276       // Handle operation-specific fields and filter creation.
277       std::string filter_type;
278       if (!rule_config.GetObjectAsString("type", filter_type)) {
279         DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'type' element missing.");
280         return false;
281       }
282       DNBLogThreadedIf(LOG_DARWIN_LOG, "Reading filter of type '%s'",
283                        filter_type.c_str());
284
285       LogFilterSP filter_sp;
286       if (filter_type == "regex") {
287         // Grab the regex for the match.
288         std::string regex;
289         if (!rule_config.GetObjectAsString("regex", regex)) {
290           DNBLogError("Regex filter missing 'regex' element.");
291           return false;
292         }
293         DNBLogThreadedIf(LOG_DARWIN_LOG, "regex for filter: \"%s\"",
294                          regex.c_str());
295
296         // Create the regex filter.
297         auto regex_filter =
298             new LogFilterRegex(filter_accepts, target_enum, regex);
299         filter_sp.reset(regex_filter);
300
301         // Validate that the filter is okay.
302         if (!regex_filter->IsValid()) {
303           DNBLogError("Invalid regex in filter: "
304                       "regex=\"%s\", error=%s",
305                       regex.c_str(), regex_filter->GetErrorAsCString());
306           return false;
307         }
308       } else if (filter_type == "match") {
309         // Grab the regex for the match.
310         std::string exact_text;
311         if (!rule_config.GetObjectAsString("exact_text", exact_text)) {
312           DNBLogError("Exact match filter missing "
313                       "'exact_text' element.");
314           return false;
315         }
316
317         // Create the filter.
318         filter_sp.reset(
319             new LogFilterExactMatch(filter_accepts, target_enum, exact_text));
320       }
321
322       // Add the filter to the chain.
323       m_filter_chain_sp->AppendFilter(filter_sp);
324     }
325     return true;
326   }
327
328   bool IsValid() const { return m_is_valid; }
329
330   os_activity_stream_flag_t GetActivityStreamFlags() const {
331     return m_activity_stream_flags;
332   }
333
334   const LogFilterChainSP &GetLogFilterChain() const {
335     return m_filter_chain_sp;
336   }
337
338 private:
339   bool m_is_valid;
340   os_activity_stream_flag_t m_activity_stream_flags;
341   LogFilterChainSP m_filter_chain_sp;
342 };
343 }
344
345 bool DarwinLogCollector::IsSupported() {
346   // We're supported if we have successfully looked up the SPI entry points.
347   return LookupSPICalls();
348 }
349
350 bool DarwinLogCollector::StartCollectingForProcess(nub_process_t pid,
351                                                    const JSONObject &config) {
352   // If we're currently collecting for this process, kill the existing
353   // collector.
354   if (CancelStreamForProcess(pid)) {
355     DNBLogThreadedIf(LOG_DARWIN_LOG,
356                      "%s() killed existing DarwinLog collector for pid %d.",
357                      __FUNCTION__, pid);
358   }
359
360   // If the process isn't alive, we're done.
361   if (!DNBProcessIsAlive(pid)) {
362     DNBLogThreadedIf(LOG_DARWIN_LOG,
363                      "%s() cannot collect for pid %d: process not alive.",
364                      __FUNCTION__, pid);
365     return false;
366   }
367
368   // Validate the configuration.
369   auto spi_config = Configuration(config);
370   if (!spi_config.IsValid()) {
371     DNBLogThreadedIf(LOG_DARWIN_LOG,
372                      "%s() invalid configuration, will not enable log "
373                      "collection",
374                      __FUNCTION__);
375     return false;
376   }
377
378   // Create the stream collector that will manage collected data
379   // for this pid.
380   DarwinLogCollectorSP collector_sp(
381       new DarwinLogCollector(pid, spi_config.GetLogFilterChain()));
382   std::weak_ptr<DarwinLogCollector> collector_wp(collector_sp);
383
384   // Setup the stream handling block.
385   os_activity_stream_block_t block =
386       ^bool(os_activity_stream_entry_t entry, int error) {
387         // Check if our collector is still alive.
388         DarwinLogCollectorSP inner_collector_sp = collector_wp.lock();
389         if (!inner_collector_sp)
390           return false;
391         return inner_collector_sp->HandleStreamEntry(entry, error);
392       };
393
394   os_activity_stream_event_block_t stream_event_block = ^void(
395       os_activity_stream_t stream, os_activity_stream_event_t event) {
396     switch (event) {
397     case OS_ACTIVITY_STREAM_EVENT_STARTED:
398       DNBLogThreadedIf(LOG_DARWIN_LOG,
399                        "received stream event: "
400                        "OS_ACTIVITY_STREAM_EVENT_STARTED, stream %p.",
401                        (void *)stream);
402       break;
403     case OS_ACTIVITY_STREAM_EVENT_STOPPED:
404       DNBLogThreadedIf(LOG_DARWIN_LOG,
405                        "received stream event: "
406                        "OS_ACTIVITY_STREAM_EVENT_STOPPED, stream %p.",
407                        (void *)stream);
408       break;
409     case OS_ACTIVITY_STREAM_EVENT_FAILED:
410       DNBLogThreadedIf(LOG_DARWIN_LOG,
411                        "received stream event: "
412                        "OS_ACTIVITY_STREAM_EVENT_FAILED, stream %p.",
413                        (void *)stream);
414       break;
415     case OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED:
416       DNBLogThreadedIf(LOG_DARWIN_LOG,
417                        "received stream event: "
418                        "OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED, stream %p.",
419                        (void *)stream);
420       break;
421     case OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED:
422       DNBLogThreadedIf(LOG_DARWIN_LOG,
423                        "received stream event: "
424                        "OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED, stream %p.",
425                        (void *)stream);
426       break;
427     }
428   };
429
430   // Create the stream.
431   os_activity_stream_t activity_stream = (*s_os_activity_stream_for_pid)(
432       pid, spi_config.GetActivityStreamFlags(), block);
433   collector_sp->SetActivityStream(activity_stream);
434
435   // Specify the stream-related event handler.
436   (*s_os_activity_stream_set_event_handler)(activity_stream,
437                                             stream_event_block);
438
439   // Start the stream.
440   (*s_os_activity_stream_resume)(activity_stream);
441
442   TrackCollector(collector_sp);
443   return true;
444 }
445
446 DarwinLogEventVector
447 DarwinLogCollector::GetEventsForProcess(nub_process_t pid) {
448   auto collector_sp = FindCollectorForProcess(pid);
449   if (!collector_sp) {
450     // We're not tracking a stream for this process.
451     return DarwinLogEventVector();
452   }
453
454   return collector_sp->RemoveEvents();
455 }
456
457 bool DarwinLogCollector::CancelStreamForProcess(nub_process_t pid) {
458   auto collector_sp = FindCollectorForProcess(pid);
459   if (!collector_sp) {
460     // We're not tracking a stream for this process.
461     return false;
462   }
463
464   collector_sp->CancelActivityStream();
465   StopTrackingCollector(collector_sp);
466
467   return true;
468 }
469
470 const char *
471 DarwinLogCollector::GetActivityForID(os_activity_id_t activity_id) const {
472   auto find_it = m_activity_map.find(activity_id);
473   return (find_it != m_activity_map.end()) ? find_it->second.m_name.c_str()
474                                            : nullptr;
475 }
476
477 /// Retrieve the full parent-child chain for activity names.  These
478 /// can be arbitrarily deep.  This method assumes the caller has already
479 /// locked the activity mutex.
480 void DarwinLogCollector::GetActivityChainForID_internal(
481     os_activity_id_t activity_id, std::string &result, size_t depth) const {
482   if (depth > MAX_ACTIVITY_CHAIN_DEPTH) {
483     // Terminating condition - too deeply nested.
484     return;
485   } else if (activity_id == 0) {
486     // Terminating condition - no activity.
487     return;
488   }
489
490   auto find_it = m_activity_map.find(activity_id);
491   if (find_it == m_activity_map.end()) {
492     // Terminating condition - no data for activity_id.
493     return;
494   }
495
496   // Activity name becomes parent activity name chain + ':' + our activity
497   // name.
498   GetActivityChainForID_internal(find_it->second.m_parent_id, result,
499                                  depth + 1);
500   if (!result.empty())
501     result += ':';
502   result += find_it->second.m_name;
503 }
504
505 std::string
506 DarwinLogCollector::GetActivityChainForID(os_activity_id_t activity_id) const {
507   std::string result;
508   {
509     std::lock_guard<std::mutex> locker(m_activity_info_mutex);
510     GetActivityChainForID_internal(activity_id, result, 1);
511   }
512   return result;
513 }
514
515 DarwinLogCollector::DarwinLogCollector(nub_process_t pid,
516                                        const LogFilterChainSP &filter_chain_sp)
517     : ActivityStore(), m_pid(pid), m_activity_stream(0), m_events(),
518       m_events_mutex(), m_filter_chain_sp(filter_chain_sp),
519       m_activity_info_mutex(), m_activity_map() {}
520
521 DarwinLogCollector::~DarwinLogCollector() {
522   // Cancel the stream.
523   if (m_activity_stream) {
524     DNBLogThreadedIf(LOG_DARWIN_LOG, "tearing down activity stream "
525                                      "collector for %d",
526                      m_pid);
527     (*s_os_activity_stream_cancel)(m_activity_stream);
528     m_activity_stream = 0;
529   } else {
530     DNBLogThreadedIf(LOG_DARWIN_LOG, "no stream to tear down for %d", m_pid);
531   }
532 }
533
534 void DarwinLogCollector::SignalDataAvailable() {
535   RNBRemoteSP remoteSP(g_remoteSP);
536   if (!remoteSP) {
537     // We're done.  This is unexpected.
538     StopTrackingCollector(shared_from_this());
539     return;
540   }
541
542   RNBContext &ctx = remoteSP->Context();
543   ctx.Events().SetEvents(RNBContext::event_darwin_log_data_available);
544   // Wait for the main thread to consume this notification if it requested
545   // we wait for it.
546   ctx.Events().WaitForResetAck(RNBContext::event_darwin_log_data_available);
547 }
548
549 void DarwinLogCollector::SetActivityStream(
550     os_activity_stream_t activity_stream) {
551   m_activity_stream = activity_stream;
552 }
553
554 bool DarwinLogCollector::HandleStreamEntry(os_activity_stream_entry_t entry,
555                                            int error) {
556   if ((error == 0) && (entry != nullptr)) {
557     if (entry->pid != m_pid) {
558       // For now, skip messages not originating from our process.
559       // Later we might want to keep all messages related to an event
560       // that we're tracking, even when it came from another process,
561       // possibly doing work on our behalf.
562       return true;
563     }
564
565     switch (entry->type) {
566     case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE:
567       DNBLogThreadedIf(
568           LOG_DARWIN_LOG, "received activity create: "
569                           "%s, creator aid %" PRIu64 ", unique_pid %" PRIu64
570                           "(activity id=%" PRIu64 ", parent id=%" PRIu64 ")",
571           entry->activity_create.name, entry->activity_create.creator_aid,
572           entry->activity_create.unique_pid, entry->activity_id,
573           entry->parent_id);
574       {
575         std::lock_guard<std::mutex> locker(m_activity_info_mutex);
576         m_activity_map.insert(
577             std::make_pair(entry->activity_id,
578                            ActivityInfo(entry->activity_create.name,
579                                         entry->activity_id, entry->parent_id)));
580       }
581       break;
582
583     case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION:
584       DNBLogThreadedIf(
585           LOG_DARWIN_LOG, "received activity transition:"
586                           "new aid: %" PRIu64 "(activity id=%" PRIu64
587                           ", parent id=%" PRIu64 ", tid %" PRIu64 ")",
588           entry->activity_transition.transition_id, entry->activity_id,
589           entry->parent_id, entry->activity_transition.thread);
590       break;
591
592     case OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE: {
593       DNBLogThreadedIf(
594           LOG_DARWIN_LOG, "received log message: "
595                           "(activity id=%" PRIu64 ", parent id=%" PRIu64 ", "
596                           "tid %" PRIu64 "): format %s",
597           entry->activity_id, entry->parent_id, entry->log_message.thread,
598           entry->log_message.format ? entry->log_message.format
599                                     : "<invalid-format>");
600
601       // Do the real work here.
602       {
603         // Ensure our process is still alive.  If not, we can
604         // cancel the collection.
605         if (!DNBProcessIsAlive(m_pid)) {
606           // We're outta here.  This is the manner in which we
607           // stop collecting for a process.
608           StopTrackingCollector(shared_from_this());
609           return false;
610         }
611
612         LogMessageOsLog os_log_message(*this, *entry);
613         if (!m_filter_chain_sp ||
614             !m_filter_chain_sp->GetAcceptMessage(os_log_message)) {
615           // This log message was rejected by the filter,
616           // so stop processing it now.
617           return true;
618         }
619
620         // Copy over the relevant bits from the message.
621         const struct os_log_message_s &log_message = entry->log_message;
622
623         DarwinLogEventSP message_sp(new DarwinLogEvent());
624         // Indicate this event is a log message event.
625         message_sp->AddStringItem("type", "log");
626
627         // Add the message contents (fully expanded).
628         // Consider expanding on the remote side.
629         // Then we don't pay for expansion until when it is
630         // used.
631         const char *message_text = os_log_message.GetMessage();
632         if (message_text)
633           message_sp->AddStringItem("message", message_text);
634
635         // Add some useful data fields.
636         message_sp->AddIntegerItem("timestamp", log_message.timestamp);
637
638         // Do we want to do all activity name resolution on this
639         // side?  Maybe.  For now, send IDs and ID->name mappings
640         // and fix this up on that side.  Later, when we add
641         // debugserver-side filtering, we'll want to get the
642         // activity names over here, so we should probably
643         // just send them as resolved strings.
644         message_sp->AddIntegerItem("activity_id", entry->activity_id);
645         message_sp->AddIntegerItem("parent_id", entry->parent_id);
646         message_sp->AddIntegerItem("thread_id", log_message.thread);
647         if (log_message.subsystem && strlen(log_message.subsystem) > 0)
648           message_sp->AddStringItem("subsystem", log_message.subsystem);
649         if (log_message.category && strlen(log_message.category) > 0)
650           message_sp->AddStringItem("category", log_message.category);
651         if (entry->activity_id != 0) {
652           std::string activity_chain =
653               GetActivityChainForID(entry->activity_id);
654           if (!activity_chain.empty())
655             message_sp->AddStringItem("activity-chain", activity_chain);
656         }
657
658         // Add it to the list for later collection.
659         {
660           std::lock_guard<std::mutex> locker(m_events_mutex);
661           m_events.push_back(message_sp);
662         }
663         SignalDataAvailable();
664       }
665       break;
666     }
667     }
668   } else {
669     DNBLogThreadedIf(LOG_DARWIN_LOG, "HandleStreamEntry: final call, "
670                                      "error %d",
671                      error);
672   }
673   return true;
674 }
675
676 DarwinLogEventVector DarwinLogCollector::RemoveEvents() {
677   DarwinLogEventVector returned_events;
678   {
679     std::lock_guard<std::mutex> locker(m_events_mutex);
680     returned_events.swap(m_events);
681   }
682   DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLogCollector::%s(): removing %lu "
683                                    "queued log entries",
684                    __FUNCTION__, returned_events.size());
685   return returned_events;
686 }
687
688 void DarwinLogCollector::CancelActivityStream() {
689   if (!m_activity_stream)
690     return;
691
692   DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLogCollector::%s(): canceling "
693                                    "activity stream %p",
694                    __FUNCTION__, m_activity_stream);
695   (*s_os_activity_stream_cancel)(m_activity_stream);
696   m_activity_stream = nullptr;
697 }