1 //===-- SystemRuntimeMacOSX.cpp -------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Plugins/Process/Utility/HistoryThread.h"
10 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
11 #include "lldb/Breakpoint/StoppointCallbackContext.h"
12 #include "lldb/Core/Module.h"
13 #include "lldb/Core/ModuleSpec.h"
14 #include "lldb/Core/PluginManager.h"
15 #include "lldb/Core/Section.h"
16 #include "lldb/Symbol/ObjectFile.h"
17 #include "lldb/Symbol/SymbolContext.h"
18 #include "lldb/Target/Process.h"
19 #include "lldb/Target/ProcessStructReader.h"
20 #include "lldb/Target/Queue.h"
21 #include "lldb/Target/QueueList.h"
22 #include "lldb/Target/Target.h"
23 #include "lldb/Target/Thread.h"
24 #include "lldb/Utility/DataBufferHeap.h"
25 #include "lldb/Utility/DataExtractor.h"
26 #include "lldb/Utility/FileSpec.h"
27 #include "lldb/Utility/LLDBLog.h"
28 #include "lldb/Utility/Log.h"
29 #include "lldb/Utility/StreamString.h"
30 
31 #include "SystemRuntimeMacOSX.h"
32 
33 #include <memory>
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 
LLDB_PLUGIN_DEFINE(SystemRuntimeMacOSX)38 LLDB_PLUGIN_DEFINE(SystemRuntimeMacOSX)
39 
40 // Create an instance of this class. This function is filled into the plugin
41 // info class that gets handed out by the plugin factory and allows the lldb to
42 // instantiate an instance of this class.
43 SystemRuntime *SystemRuntimeMacOSX::CreateInstance(Process *process) {
44   bool create = false;
45   if (!create) {
46     create = true;
47     Module *exe_module = process->GetTarget().GetExecutableModulePointer();
48     if (exe_module) {
49       ObjectFile *object_file = exe_module->GetObjectFile();
50       if (object_file) {
51         create = (object_file->GetStrata() == ObjectFile::eStrataUser);
52       }
53     }
54 
55     if (create) {
56       const llvm::Triple &triple_ref =
57           process->GetTarget().GetArchitecture().GetTriple();
58       switch (triple_ref.getOS()) {
59       case llvm::Triple::Darwin:
60       case llvm::Triple::MacOSX:
61       case llvm::Triple::IOS:
62       case llvm::Triple::TvOS:
63       case llvm::Triple::WatchOS:
64       // NEED_BRIDGEOS_TRIPLE case llvm::Triple::BridgeOS:
65         create = triple_ref.getVendor() == llvm::Triple::Apple;
66         break;
67       default:
68         create = false;
69         break;
70       }
71     }
72   }
73 
74   if (create)
75     return new SystemRuntimeMacOSX(process);
76   return nullptr;
77 }
78 
79 // Constructor
SystemRuntimeMacOSX(Process * process)80 SystemRuntimeMacOSX::SystemRuntimeMacOSX(Process *process)
81     : SystemRuntime(process), m_break_id(LLDB_INVALID_BREAK_ID), m_mutex(),
82       m_get_queues_handler(process), m_get_pending_items_handler(process),
83       m_get_item_info_handler(process), m_get_thread_item_info_handler(process),
84       m_page_to_free(LLDB_INVALID_ADDRESS), m_page_to_free_size(0),
85       m_lib_backtrace_recording_info(),
86       m_dispatch_queue_offsets_addr(LLDB_INVALID_ADDRESS),
87       m_libdispatch_offsets(),
88       m_libpthread_layout_offsets_addr(LLDB_INVALID_ADDRESS),
89       m_libpthread_offsets(), m_dispatch_tsd_indexes_addr(LLDB_INVALID_ADDRESS),
90       m_libdispatch_tsd_indexes(),
91       m_dispatch_voucher_offsets_addr(LLDB_INVALID_ADDRESS),
92       m_libdispatch_voucher_offsets() {}
93 
94 // Destructor
~SystemRuntimeMacOSX()95 SystemRuntimeMacOSX::~SystemRuntimeMacOSX() { Clear(true); }
96 
Detach()97 void SystemRuntimeMacOSX::Detach() {
98   m_get_queues_handler.Detach();
99   m_get_pending_items_handler.Detach();
100   m_get_item_info_handler.Detach();
101   m_get_thread_item_info_handler.Detach();
102 }
103 
104 // Clear out the state of this class.
Clear(bool clear_process)105 void SystemRuntimeMacOSX::Clear(bool clear_process) {
106   std::lock_guard<std::recursive_mutex> guard(m_mutex);
107 
108   if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id))
109     m_process->ClearBreakpointSiteByID(m_break_id);
110 
111   if (clear_process)
112     m_process = nullptr;
113   m_break_id = LLDB_INVALID_BREAK_ID;
114 }
115 
116 std::string
GetQueueNameFromThreadQAddress(addr_t dispatch_qaddr)117 SystemRuntimeMacOSX::GetQueueNameFromThreadQAddress(addr_t dispatch_qaddr) {
118   std::string dispatch_queue_name;
119   if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0)
120     return "";
121 
122   ReadLibdispatchOffsets();
123   if (m_libdispatch_offsets.IsValid()) {
124     // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a
125     // thread - deref it to get the address of the dispatch_queue_t structure
126     // for this thread's queue.
127     Status error;
128     addr_t dispatch_queue_addr =
129         m_process->ReadPointerFromMemory(dispatch_qaddr, error);
130     if (error.Success()) {
131       if (m_libdispatch_offsets.dqo_version >= 4) {
132         // libdispatch versions 4+, pointer to dispatch name is in the queue
133         // structure.
134         addr_t pointer_to_label_address =
135             dispatch_queue_addr + m_libdispatch_offsets.dqo_label;
136         addr_t label_addr =
137             m_process->ReadPointerFromMemory(pointer_to_label_address, error);
138         if (error.Success()) {
139           m_process->ReadCStringFromMemory(label_addr, dispatch_queue_name,
140                                            error);
141         }
142       } else {
143         // libdispatch versions 1-3, dispatch name is a fixed width char array
144         // in the queue structure.
145         addr_t label_addr =
146             dispatch_queue_addr + m_libdispatch_offsets.dqo_label;
147         dispatch_queue_name.resize(m_libdispatch_offsets.dqo_label_size, '\0');
148         size_t bytes_read =
149             m_process->ReadMemory(label_addr, &dispatch_queue_name[0],
150                                   m_libdispatch_offsets.dqo_label_size, error);
151         if (bytes_read < m_libdispatch_offsets.dqo_label_size)
152           dispatch_queue_name.erase(bytes_read);
153       }
154     }
155   }
156   return dispatch_queue_name;
157 }
158 
GetLibdispatchQueueAddressFromThreadQAddress(addr_t dispatch_qaddr)159 lldb::addr_t SystemRuntimeMacOSX::GetLibdispatchQueueAddressFromThreadQAddress(
160     addr_t dispatch_qaddr) {
161   addr_t libdispatch_queue_t_address = LLDB_INVALID_ADDRESS;
162   Status error;
163   libdispatch_queue_t_address =
164       m_process->ReadPointerFromMemory(dispatch_qaddr, error);
165   if (!error.Success()) {
166     libdispatch_queue_t_address = LLDB_INVALID_ADDRESS;
167   }
168   return libdispatch_queue_t_address;
169 }
170 
GetQueueKind(addr_t dispatch_queue_addr)171 lldb::QueueKind SystemRuntimeMacOSX::GetQueueKind(addr_t dispatch_queue_addr) {
172   if (dispatch_queue_addr == LLDB_INVALID_ADDRESS || dispatch_queue_addr == 0)
173     return eQueueKindUnknown;
174 
175   QueueKind kind = eQueueKindUnknown;
176   ReadLibdispatchOffsets();
177   if (m_libdispatch_offsets.IsValid() &&
178       m_libdispatch_offsets.dqo_version >= 4) {
179     Status error;
180     uint64_t width = m_process->ReadUnsignedIntegerFromMemory(
181         dispatch_queue_addr + m_libdispatch_offsets.dqo_width,
182         m_libdispatch_offsets.dqo_width_size, 0, error);
183     if (error.Success()) {
184       if (width == 1) {
185         kind = eQueueKindSerial;
186       }
187       if (width > 1) {
188         kind = eQueueKindConcurrent;
189       }
190     }
191   }
192   return kind;
193 }
194 
AddThreadExtendedInfoPacketHints(lldb_private::StructuredData::ObjectSP dict_sp)195 void SystemRuntimeMacOSX::AddThreadExtendedInfoPacketHints(
196     lldb_private::StructuredData::ObjectSP dict_sp) {
197   StructuredData::Dictionary *dict = dict_sp->GetAsDictionary();
198   if (dict) {
199     ReadLibpthreadOffsets();
200     if (m_libpthread_offsets.IsValid()) {
201       dict->AddIntegerItem("plo_pthread_tsd_base_offset",
202                            m_libpthread_offsets.plo_pthread_tsd_base_offset);
203       dict->AddIntegerItem(
204           "plo_pthread_tsd_base_address_offset",
205           m_libpthread_offsets.plo_pthread_tsd_base_address_offset);
206       dict->AddIntegerItem("plo_pthread_tsd_entry_size",
207                            m_libpthread_offsets.plo_pthread_tsd_entry_size);
208     }
209 
210     ReadLibdispatchTSDIndexes();
211     if (m_libdispatch_tsd_indexes.IsValid()) {
212       dict->AddIntegerItem("dti_queue_index",
213                            m_libdispatch_tsd_indexes.dti_queue_index);
214       dict->AddIntegerItem("dti_voucher_index",
215                            m_libdispatch_tsd_indexes.dti_voucher_index);
216       dict->AddIntegerItem("dti_qos_class_index",
217                            m_libdispatch_tsd_indexes.dti_qos_class_index);
218     }
219   }
220 }
221 
SafeToCallFunctionsOnThisThread(ThreadSP thread_sp)222 bool SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread(ThreadSP thread_sp) {
223   if (thread_sp && thread_sp->GetStackFrameCount() > 0 &&
224       thread_sp->GetFrameWithConcreteFrameIndex(0)) {
225     const SymbolContext sym_ctx(
226         thread_sp->GetFrameWithConcreteFrameIndex(0)->GetSymbolContext(
227             eSymbolContextSymbol));
228     static ConstString g_select_symbol("__select");
229     if (sym_ctx.GetFunctionName() == g_select_symbol) {
230       return false;
231     }
232   }
233   return true;
234 }
235 
236 lldb::queue_id_t
GetQueueIDFromThreadQAddress(lldb::addr_t dispatch_qaddr)237 SystemRuntimeMacOSX::GetQueueIDFromThreadQAddress(lldb::addr_t dispatch_qaddr) {
238   queue_id_t queue_id = LLDB_INVALID_QUEUE_ID;
239 
240   if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0)
241     return queue_id;
242 
243   ReadLibdispatchOffsets();
244   if (m_libdispatch_offsets.IsValid()) {
245     // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a
246     // thread - deref it to get the address of the dispatch_queue_t structure
247     // for this thread's queue.
248     Status error;
249     uint64_t dispatch_queue_addr =
250         m_process->ReadPointerFromMemory(dispatch_qaddr, error);
251     if (error.Success()) {
252       addr_t serialnum_address =
253           dispatch_queue_addr + m_libdispatch_offsets.dqo_serialnum;
254       queue_id_t serialnum = m_process->ReadUnsignedIntegerFromMemory(
255           serialnum_address, m_libdispatch_offsets.dqo_serialnum_size,
256           LLDB_INVALID_QUEUE_ID, error);
257       if (error.Success()) {
258         queue_id = serialnum;
259       }
260     }
261   }
262 
263   return queue_id;
264 }
265 
ReadLibdispatchOffsetsAddress()266 void SystemRuntimeMacOSX::ReadLibdispatchOffsetsAddress() {
267   if (m_dispatch_queue_offsets_addr != LLDB_INVALID_ADDRESS)
268     return;
269 
270   static ConstString g_dispatch_queue_offsets_symbol_name(
271       "dispatch_queue_offsets");
272   const Symbol *dispatch_queue_offsets_symbol = nullptr;
273 
274   // libdispatch symbols were in libSystem.B.dylib up through Mac OS X 10.6
275   // ("Snow Leopard")
276   ModuleSpec libSystem_module_spec(FileSpec("libSystem.B.dylib"));
277   ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule(
278       libSystem_module_spec));
279   if (module_sp)
280     dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType(
281         g_dispatch_queue_offsets_symbol_name, eSymbolTypeData);
282 
283   // libdispatch symbols are in their own dylib as of Mac OS X 10.7 ("Lion")
284   // and later
285   if (dispatch_queue_offsets_symbol == nullptr) {
286     ModuleSpec libdispatch_module_spec(FileSpec("libdispatch.dylib"));
287     module_sp = m_process->GetTarget().GetImages().FindFirstModule(
288         libdispatch_module_spec);
289     if (module_sp)
290       dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType(
291           g_dispatch_queue_offsets_symbol_name, eSymbolTypeData);
292   }
293   if (dispatch_queue_offsets_symbol)
294     m_dispatch_queue_offsets_addr =
295         dispatch_queue_offsets_symbol->GetLoadAddress(&m_process->GetTarget());
296 }
297 
ReadLibdispatchOffsets()298 void SystemRuntimeMacOSX::ReadLibdispatchOffsets() {
299   if (m_libdispatch_offsets.IsValid())
300     return;
301 
302   ReadLibdispatchOffsetsAddress();
303 
304   uint8_t memory_buffer[sizeof(struct LibdispatchOffsets)];
305   DataExtractor data(memory_buffer, sizeof(memory_buffer),
306                      m_process->GetByteOrder(),
307                      m_process->GetAddressByteSize());
308 
309   Status error;
310   if (m_process->ReadMemory(m_dispatch_queue_offsets_addr, memory_buffer,
311                             sizeof(memory_buffer),
312                             error) == sizeof(memory_buffer)) {
313     lldb::offset_t data_offset = 0;
314 
315     // The struct LibdispatchOffsets is a series of uint16_t's - extract them
316     // all in one big go.
317     data.GetU16(&data_offset, &m_libdispatch_offsets.dqo_version,
318                 sizeof(struct LibdispatchOffsets) / sizeof(uint16_t));
319   }
320 }
321 
ReadLibpthreadOffsetsAddress()322 void SystemRuntimeMacOSX::ReadLibpthreadOffsetsAddress() {
323   if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS)
324     return;
325 
326   static ConstString g_libpthread_layout_offsets_symbol_name(
327       "pthread_layout_offsets");
328   const Symbol *libpthread_layout_offsets_symbol = nullptr;
329 
330   ModuleSpec libpthread_module_spec(FileSpec("libsystem_pthread.dylib"));
331   ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule(
332       libpthread_module_spec));
333   if (module_sp) {
334     libpthread_layout_offsets_symbol =
335         module_sp->FindFirstSymbolWithNameAndType(
336             g_libpthread_layout_offsets_symbol_name, eSymbolTypeData);
337     if (libpthread_layout_offsets_symbol) {
338       m_libpthread_layout_offsets_addr =
339           libpthread_layout_offsets_symbol->GetLoadAddress(
340               &m_process->GetTarget());
341     }
342   }
343 }
344 
ReadLibpthreadOffsets()345 void SystemRuntimeMacOSX::ReadLibpthreadOffsets() {
346   if (m_libpthread_offsets.IsValid())
347     return;
348 
349   ReadLibpthreadOffsetsAddress();
350 
351   if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) {
352     uint8_t memory_buffer[sizeof(struct LibpthreadOffsets)];
353     DataExtractor data(memory_buffer, sizeof(memory_buffer),
354                        m_process->GetByteOrder(),
355                        m_process->GetAddressByteSize());
356     Status error;
357     if (m_process->ReadMemory(m_libpthread_layout_offsets_addr, memory_buffer,
358                               sizeof(memory_buffer),
359                               error) == sizeof(memory_buffer)) {
360       lldb::offset_t data_offset = 0;
361 
362       // The struct LibpthreadOffsets is a series of uint16_t's - extract them
363       // all in one big go.
364       data.GetU16(&data_offset, &m_libpthread_offsets.plo_version,
365                   sizeof(struct LibpthreadOffsets) / sizeof(uint16_t));
366     }
367   }
368 }
369 
ReadLibdispatchTSDIndexesAddress()370 void SystemRuntimeMacOSX::ReadLibdispatchTSDIndexesAddress() {
371   if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS)
372     return;
373 
374   static ConstString g_libdispatch_tsd_indexes_symbol_name(
375       "dispatch_tsd_indexes");
376   const Symbol *libdispatch_tsd_indexes_symbol = nullptr;
377 
378   ModuleSpec libpthread_module_spec(FileSpec("libdispatch.dylib"));
379   ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule(
380       libpthread_module_spec));
381   if (module_sp) {
382     libdispatch_tsd_indexes_symbol = module_sp->FindFirstSymbolWithNameAndType(
383         g_libdispatch_tsd_indexes_symbol_name, eSymbolTypeData);
384     if (libdispatch_tsd_indexes_symbol) {
385       m_dispatch_tsd_indexes_addr =
386           libdispatch_tsd_indexes_symbol->GetLoadAddress(
387               &m_process->GetTarget());
388     }
389   }
390 }
391 
ReadLibdispatchTSDIndexes()392 void SystemRuntimeMacOSX::ReadLibdispatchTSDIndexes() {
393   if (m_libdispatch_tsd_indexes.IsValid())
394     return;
395 
396   ReadLibdispatchTSDIndexesAddress();
397 
398   if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) {
399 
400 // We don't need to check the version number right now, it will be at least 2,
401 // but keep this code around to fetch just the version # for the future where
402 // we need to fetch alternate versions of the struct.
403 #if 0
404         uint16_t dti_version = 2;
405         Address dti_struct_addr;
406         if (m_process->GetTarget().ResolveLoadAddress (m_dispatch_tsd_indexes_addr, dti_struct_addr))
407         {
408             Status error;
409             uint16_t version = m_process->GetTarget().ReadUnsignedIntegerFromMemory (dti_struct_addr, false, 2, UINT16_MAX, error);
410             if (error.Success() && dti_version != UINT16_MAX)
411             {
412                 dti_version = version;
413             }
414         }
415 #endif
416 
417     TypeSystemClangSP scratch_ts_sp =
418         ScratchTypeSystemClang::GetForTarget(m_process->GetTarget());
419     if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) {
420       CompilerType uint16 =
421           scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 16);
422       CompilerType dispatch_tsd_indexes_s = scratch_ts_sp->CreateRecordType(
423           nullptr, OptionalClangModuleID(), lldb::eAccessPublic,
424           "__lldb_dispatch_tsd_indexes_s", clang::TTK_Struct,
425           lldb::eLanguageTypeC);
426 
427       TypeSystemClang::StartTagDeclarationDefinition(dispatch_tsd_indexes_s);
428       TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s,
429                                             "dti_version", uint16,
430                                             lldb::eAccessPublic, 0);
431       TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s,
432                                             "dti_queue_index", uint16,
433                                             lldb::eAccessPublic, 0);
434       TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s,
435                                             "dti_voucher_index", uint16,
436                                             lldb::eAccessPublic, 0);
437       TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s,
438                                             "dti_qos_class_index", uint16,
439                                             lldb::eAccessPublic, 0);
440       TypeSystemClang::CompleteTagDeclarationDefinition(dispatch_tsd_indexes_s);
441 
442       ProcessStructReader struct_reader(m_process, m_dispatch_tsd_indexes_addr,
443                                         dispatch_tsd_indexes_s);
444 
445       m_libdispatch_tsd_indexes.dti_version =
446           struct_reader.GetField<uint16_t>(ConstString("dti_version"));
447       m_libdispatch_tsd_indexes.dti_queue_index =
448           struct_reader.GetField<uint16_t>(ConstString("dti_queue_index"));
449       m_libdispatch_tsd_indexes.dti_voucher_index =
450           struct_reader.GetField<uint16_t>(ConstString("dti_voucher_index"));
451       m_libdispatch_tsd_indexes.dti_qos_class_index =
452           struct_reader.GetField<uint16_t>(ConstString("dti_qos_class_index"));
453     }
454   }
455 }
456 
GetExtendedBacktraceThread(ThreadSP real_thread,ConstString type)457 ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceThread(ThreadSP real_thread,
458                                                          ConstString type) {
459   ThreadSP originating_thread_sp;
460   if (BacktraceRecordingHeadersInitialized() && type == "libdispatch") {
461     Status error;
462 
463     // real_thread is either an actual, live thread (in which case we need to
464     // call into libBacktraceRecording to find its originator) or it is an
465     // extended backtrace itself, in which case we get the token from it and
466     // call into libBacktraceRecording to find the originator of that token.
467 
468     if (real_thread->GetExtendedBacktraceToken() != LLDB_INVALID_ADDRESS) {
469       originating_thread_sp = GetExtendedBacktraceFromItemRef(
470           real_thread->GetExtendedBacktraceToken());
471     } else {
472       ThreadSP cur_thread_sp(
473           m_process->GetThreadList().GetExpressionExecutionThread());
474       AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo ret =
475           m_get_thread_item_info_handler.GetThreadItemInfo(
476               *cur_thread_sp.get(), real_thread->GetID(), m_page_to_free,
477               m_page_to_free_size, error);
478       m_page_to_free = LLDB_INVALID_ADDRESS;
479       m_page_to_free_size = 0;
480       if (ret.item_buffer_ptr != 0 &&
481           ret.item_buffer_ptr != LLDB_INVALID_ADDRESS &&
482           ret.item_buffer_size > 0) {
483         DataBufferHeap data(ret.item_buffer_size, 0);
484         if (m_process->ReadMemory(ret.item_buffer_ptr, data.GetBytes(),
485                                   ret.item_buffer_size, error) &&
486             error.Success()) {
487           DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
488                                   m_process->GetByteOrder(),
489                                   m_process->GetAddressByteSize());
490           ItemInfo item = ExtractItemInfoFromBuffer(extractor);
491           originating_thread_sp = std::make_shared<HistoryThread>(
492               *m_process, item.enqueuing_thread_id, item.enqueuing_callstack);
493           originating_thread_sp->SetExtendedBacktraceToken(
494               item.item_that_enqueued_this);
495           originating_thread_sp->SetQueueName(
496               item.enqueuing_queue_label.c_str());
497           originating_thread_sp->SetQueueID(item.enqueuing_queue_serialnum);
498           //                    originating_thread_sp->SetThreadName
499           //                    (item.enqueuing_thread_label.c_str());
500         }
501         m_page_to_free = ret.item_buffer_ptr;
502         m_page_to_free_size = ret.item_buffer_size;
503       }
504     }
505   } else if (type == "Application Specific Backtrace") {
506     StructuredData::ObjectSP thread_extended_sp =
507         real_thread->GetExtendedInfo();
508 
509     if (!thread_extended_sp)
510       return {};
511 
512     StructuredData::Array *thread_extended_info =
513         thread_extended_sp->GetAsArray();
514 
515     if (!thread_extended_info || !thread_extended_info->GetSize())
516       return {};
517 
518     std::vector<addr_t> app_specific_backtrace_pcs;
519 
520     auto extract_frame_pc =
521         [&app_specific_backtrace_pcs](StructuredData::Object *obj) -> bool {
522       if (!obj)
523         return false;
524 
525       StructuredData::Dictionary *dict = obj->GetAsDictionary();
526       if (!dict)
527         return false;
528 
529       lldb::addr_t pc = LLDB_INVALID_ADDRESS;
530       if (!dict->GetValueForKeyAsInteger("pc", pc))
531         return false;
532 
533       app_specific_backtrace_pcs.push_back(pc);
534 
535       return pc != LLDB_INVALID_ADDRESS;
536     };
537 
538     if (!thread_extended_info->ForEach(extract_frame_pc))
539       return {};
540 
541     originating_thread_sp =
542         std::make_shared<HistoryThread>(*m_process, real_thread->GetIndexID(),
543                                         app_specific_backtrace_pcs, true);
544     originating_thread_sp->SetQueueName(type.AsCString());
545   }
546   return originating_thread_sp;
547 }
548 
549 ThreadSP
GetExtendedBacktraceFromItemRef(lldb::addr_t item_ref)550 SystemRuntimeMacOSX::GetExtendedBacktraceFromItemRef(lldb::addr_t item_ref) {
551   ThreadSP return_thread_sp;
552 
553   AppleGetItemInfoHandler::GetItemInfoReturnInfo ret;
554   ThreadSP cur_thread_sp(
555       m_process->GetThreadList().GetExpressionExecutionThread());
556   Status error;
557   ret = m_get_item_info_handler.GetItemInfo(*cur_thread_sp.get(), item_ref,
558                                             m_page_to_free, m_page_to_free_size,
559                                             error);
560   m_page_to_free = LLDB_INVALID_ADDRESS;
561   m_page_to_free_size = 0;
562   if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS &&
563       ret.item_buffer_size > 0) {
564     DataBufferHeap data(ret.item_buffer_size, 0);
565     if (m_process->ReadMemory(ret.item_buffer_ptr, data.GetBytes(),
566                               ret.item_buffer_size, error) &&
567         error.Success()) {
568       DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
569                               m_process->GetByteOrder(),
570                               m_process->GetAddressByteSize());
571       ItemInfo item = ExtractItemInfoFromBuffer(extractor);
572       return_thread_sp = std::make_shared<HistoryThread>(
573           *m_process, item.enqueuing_thread_id, item.enqueuing_callstack);
574       return_thread_sp->SetExtendedBacktraceToken(item.item_that_enqueued_this);
575       return_thread_sp->SetQueueName(item.enqueuing_queue_label.c_str());
576       return_thread_sp->SetQueueID(item.enqueuing_queue_serialnum);
577       //            return_thread_sp->SetThreadName
578       //            (item.enqueuing_thread_label.c_str());
579 
580       m_page_to_free = ret.item_buffer_ptr;
581       m_page_to_free_size = ret.item_buffer_size;
582     }
583   }
584   return return_thread_sp;
585 }
586 
587 ThreadSP
GetExtendedBacktraceForQueueItem(QueueItemSP queue_item_sp,ConstString type)588 SystemRuntimeMacOSX::GetExtendedBacktraceForQueueItem(QueueItemSP queue_item_sp,
589                                                       ConstString type) {
590   ThreadSP extended_thread_sp;
591   if (type != "libdispatch")
592     return extended_thread_sp;
593 
594   extended_thread_sp = std::make_shared<HistoryThread>(
595       *m_process, queue_item_sp->GetEnqueueingThreadID(),
596       queue_item_sp->GetEnqueueingBacktrace());
597   extended_thread_sp->SetExtendedBacktraceToken(
598       queue_item_sp->GetItemThatEnqueuedThis());
599   extended_thread_sp->SetQueueName(queue_item_sp->GetQueueLabel().c_str());
600   extended_thread_sp->SetQueueID(queue_item_sp->GetEnqueueingQueueID());
601   //    extended_thread_sp->SetThreadName
602   //    (queue_item_sp->GetThreadLabel().c_str());
603 
604   return extended_thread_sp;
605 }
606 
607 /* Returns true if we were able to get the version / offset information
608  * out of libBacktraceRecording.  false means we were unable to retrieve
609  * this; the queue_info_version field will be 0.
610  */
611 
BacktraceRecordingHeadersInitialized()612 bool SystemRuntimeMacOSX::BacktraceRecordingHeadersInitialized() {
613   if (m_lib_backtrace_recording_info.queue_info_version != 0)
614     return true;
615 
616   addr_t queue_info_version_address = LLDB_INVALID_ADDRESS;
617   addr_t queue_info_data_offset_address = LLDB_INVALID_ADDRESS;
618   addr_t item_info_version_address = LLDB_INVALID_ADDRESS;
619   addr_t item_info_data_offset_address = LLDB_INVALID_ADDRESS;
620   Target &target = m_process->GetTarget();
621 
622   static ConstString introspection_dispatch_queue_info_version(
623       "__introspection_dispatch_queue_info_version");
624   SymbolContextList sc_list;
625   m_process->GetTarget().GetImages().FindSymbolsWithNameAndType(
626       introspection_dispatch_queue_info_version, eSymbolTypeData, sc_list);
627   if (!sc_list.IsEmpty()) {
628     SymbolContext sc;
629     sc_list.GetContextAtIndex(0, sc);
630     AddressRange addr_range;
631     sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range);
632     queue_info_version_address =
633         addr_range.GetBaseAddress().GetLoadAddress(&target);
634   }
635   sc_list.Clear();
636 
637   static ConstString introspection_dispatch_queue_info_data_offset(
638       "__introspection_dispatch_queue_info_data_offset");
639   m_process->GetTarget().GetImages().FindSymbolsWithNameAndType(
640       introspection_dispatch_queue_info_data_offset, eSymbolTypeData, sc_list);
641   if (!sc_list.IsEmpty()) {
642     SymbolContext sc;
643     sc_list.GetContextAtIndex(0, sc);
644     AddressRange addr_range;
645     sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range);
646     queue_info_data_offset_address =
647         addr_range.GetBaseAddress().GetLoadAddress(&target);
648   }
649   sc_list.Clear();
650 
651   static ConstString introspection_dispatch_item_info_version(
652       "__introspection_dispatch_item_info_version");
653   m_process->GetTarget().GetImages().FindSymbolsWithNameAndType(
654       introspection_dispatch_item_info_version, eSymbolTypeData, sc_list);
655   if (!sc_list.IsEmpty()) {
656     SymbolContext sc;
657     sc_list.GetContextAtIndex(0, sc);
658     AddressRange addr_range;
659     sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range);
660     item_info_version_address =
661         addr_range.GetBaseAddress().GetLoadAddress(&target);
662   }
663   sc_list.Clear();
664 
665   static ConstString introspection_dispatch_item_info_data_offset(
666       "__introspection_dispatch_item_info_data_offset");
667   m_process->GetTarget().GetImages().FindSymbolsWithNameAndType(
668       introspection_dispatch_item_info_data_offset, eSymbolTypeData, sc_list);
669   if (!sc_list.IsEmpty()) {
670     SymbolContext sc;
671     sc_list.GetContextAtIndex(0, sc);
672     AddressRange addr_range;
673     sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range);
674     item_info_data_offset_address =
675         addr_range.GetBaseAddress().GetLoadAddress(&target);
676   }
677 
678   if (queue_info_version_address != LLDB_INVALID_ADDRESS &&
679       queue_info_data_offset_address != LLDB_INVALID_ADDRESS &&
680       item_info_version_address != LLDB_INVALID_ADDRESS &&
681       item_info_data_offset_address != LLDB_INVALID_ADDRESS) {
682     Status error;
683     m_lib_backtrace_recording_info.queue_info_version =
684         m_process->ReadUnsignedIntegerFromMemory(queue_info_version_address, 2,
685                                                  0, error);
686     if (error.Success()) {
687       m_lib_backtrace_recording_info.queue_info_data_offset =
688           m_process->ReadUnsignedIntegerFromMemory(
689               queue_info_data_offset_address, 2, 0, error);
690       if (error.Success()) {
691         m_lib_backtrace_recording_info.item_info_version =
692             m_process->ReadUnsignedIntegerFromMemory(item_info_version_address,
693                                                      2, 0, error);
694         if (error.Success()) {
695           m_lib_backtrace_recording_info.item_info_data_offset =
696               m_process->ReadUnsignedIntegerFromMemory(
697                   item_info_data_offset_address, 2, 0, error);
698           if (!error.Success()) {
699             m_lib_backtrace_recording_info.queue_info_version = 0;
700           }
701         } else {
702           m_lib_backtrace_recording_info.queue_info_version = 0;
703         }
704       } else {
705         m_lib_backtrace_recording_info.queue_info_version = 0;
706       }
707     }
708   }
709 
710   return m_lib_backtrace_recording_info.queue_info_version != 0;
711 }
712 
713 const std::vector<ConstString> &
GetExtendedBacktraceTypes()714 SystemRuntimeMacOSX::GetExtendedBacktraceTypes() {
715   if (m_types.size() == 0) {
716     m_types.push_back(ConstString("libdispatch"));
717     m_types.push_back(ConstString("Application Specific Backtrace"));
718     // We could have pthread as another type in the future if we have a way of
719     // gathering that information & it's useful to distinguish between them.
720   }
721   return m_types;
722 }
723 
PopulateQueueList(lldb_private::QueueList & queue_list)724 void SystemRuntimeMacOSX::PopulateQueueList(
725     lldb_private::QueueList &queue_list) {
726   if (BacktraceRecordingHeadersInitialized()) {
727     AppleGetQueuesHandler::GetQueuesReturnInfo queue_info_pointer;
728     ThreadSP cur_thread_sp(
729         m_process->GetThreadList().GetExpressionExecutionThread());
730     if (cur_thread_sp) {
731       Status error;
732       queue_info_pointer = m_get_queues_handler.GetCurrentQueues(
733           *cur_thread_sp.get(), m_page_to_free, m_page_to_free_size, error);
734       m_page_to_free = LLDB_INVALID_ADDRESS;
735       m_page_to_free_size = 0;
736       if (error.Success()) {
737 
738         if (queue_info_pointer.count > 0 &&
739             queue_info_pointer.queues_buffer_size > 0 &&
740             queue_info_pointer.queues_buffer_ptr != 0 &&
741             queue_info_pointer.queues_buffer_ptr != LLDB_INVALID_ADDRESS) {
742           PopulateQueuesUsingLibBTR(queue_info_pointer.queues_buffer_ptr,
743                                     queue_info_pointer.queues_buffer_size,
744                                     queue_info_pointer.count, queue_list);
745         }
746       }
747     }
748   }
749 
750   // We either didn't have libBacktraceRecording (and need to create the queues
751   // list based on threads) or we did get the queues list from
752   // libBacktraceRecording but some special queues may not be included in its
753   // information.  This is needed because libBacktraceRecording will only list
754   // queues with pending or running items by default - but the magic com.apple
755   // .main-thread queue on thread 1 is always around.
756 
757   for (ThreadSP thread_sp : m_process->Threads()) {
758     if (thread_sp->GetAssociatedWithLibdispatchQueue() != eLazyBoolNo) {
759       if (thread_sp->GetQueueID() != LLDB_INVALID_QUEUE_ID) {
760         if (queue_list.FindQueueByID(thread_sp->GetQueueID()).get() ==
761             nullptr) {
762           QueueSP queue_sp(new Queue(m_process->shared_from_this(),
763                                      thread_sp->GetQueueID(),
764                                      thread_sp->GetQueueName()));
765           if (thread_sp->ThreadHasQueueInformation()) {
766             queue_sp->SetKind(thread_sp->GetQueueKind());
767             queue_sp->SetLibdispatchQueueAddress(
768                 thread_sp->GetQueueLibdispatchQueueAddress());
769             queue_list.AddQueue(queue_sp);
770           } else {
771             queue_sp->SetKind(
772                 GetQueueKind(thread_sp->GetQueueLibdispatchQueueAddress()));
773             queue_sp->SetLibdispatchQueueAddress(
774                 thread_sp->GetQueueLibdispatchQueueAddress());
775             queue_list.AddQueue(queue_sp);
776           }
777         }
778       }
779     }
780   }
781 }
782 
783 // Returns either an array of introspection_dispatch_item_info_ref's for the
784 // pending items on a queue or an array introspection_dispatch_item_info_ref's
785 // and code addresses for the pending items on a queue.  The information about
786 // each of these pending items then needs to be fetched individually by passing
787 // the ref to libBacktraceRecording.
788 
789 SystemRuntimeMacOSX::PendingItemsForQueue
GetPendingItemRefsForQueue(lldb::addr_t queue)790 SystemRuntimeMacOSX::GetPendingItemRefsForQueue(lldb::addr_t queue) {
791   PendingItemsForQueue pending_item_refs = {};
792   AppleGetPendingItemsHandler::GetPendingItemsReturnInfo pending_items_pointer;
793   ThreadSP cur_thread_sp(
794       m_process->GetThreadList().GetExpressionExecutionThread());
795   if (cur_thread_sp) {
796     Status error;
797     pending_items_pointer = m_get_pending_items_handler.GetPendingItems(
798         *cur_thread_sp.get(), queue, m_page_to_free, m_page_to_free_size,
799         error);
800     m_page_to_free = LLDB_INVALID_ADDRESS;
801     m_page_to_free_size = 0;
802     if (error.Success()) {
803       if (pending_items_pointer.count > 0 &&
804           pending_items_pointer.items_buffer_size > 0 &&
805           pending_items_pointer.items_buffer_ptr != 0 &&
806           pending_items_pointer.items_buffer_ptr != LLDB_INVALID_ADDRESS) {
807         DataBufferHeap data(pending_items_pointer.items_buffer_size, 0);
808         if (m_process->ReadMemory(
809                 pending_items_pointer.items_buffer_ptr, data.GetBytes(),
810                 pending_items_pointer.items_buffer_size, error)) {
811           DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
812                                   m_process->GetByteOrder(),
813                                   m_process->GetAddressByteSize());
814 
815           // We either have an array of
816           //    void* item_ref
817           // (old style) or we have a structure returned which looks like
818           //
819           // struct introspection_dispatch_pending_item_info_s {
820           //   void *item_ref;
821           //   void *function_or_block;
822           // };
823           //
824           // struct introspection_dispatch_pending_items_array_s {
825           //   uint32_t version;
826           //   uint32_t size_of_item_info;
827           //   introspection_dispatch_pending_item_info_s items[];
828           //   }
829 
830           offset_t offset = 0;
831           uint64_t i = 0;
832           uint32_t version = extractor.GetU32(&offset);
833           if (version == 1) {
834             pending_item_refs.new_style = true;
835             uint32_t item_size = extractor.GetU32(&offset);
836             uint32_t start_of_array_offset = offset;
837             while (offset < pending_items_pointer.items_buffer_size &&
838                    i < pending_items_pointer.count) {
839               offset = start_of_array_offset + (i * item_size);
840               ItemRefAndCodeAddress item;
841               item.item_ref = extractor.GetAddress(&offset);
842               item.code_address = extractor.GetAddress(&offset);
843               pending_item_refs.item_refs_and_code_addresses.push_back(item);
844               i++;
845             }
846           } else {
847             offset = 0;
848             pending_item_refs.new_style = false;
849             while (offset < pending_items_pointer.items_buffer_size &&
850                    i < pending_items_pointer.count) {
851               ItemRefAndCodeAddress item;
852               item.item_ref = extractor.GetAddress(&offset);
853               item.code_address = LLDB_INVALID_ADDRESS;
854               pending_item_refs.item_refs_and_code_addresses.push_back(item);
855               i++;
856             }
857           }
858         }
859         m_page_to_free = pending_items_pointer.items_buffer_ptr;
860         m_page_to_free_size = pending_items_pointer.items_buffer_size;
861       }
862     }
863   }
864   return pending_item_refs;
865 }
866 
PopulatePendingItemsForQueue(Queue * queue)867 void SystemRuntimeMacOSX::PopulatePendingItemsForQueue(Queue *queue) {
868   if (BacktraceRecordingHeadersInitialized()) {
869     PendingItemsForQueue pending_item_refs =
870         GetPendingItemRefsForQueue(queue->GetLibdispatchQueueAddress());
871     for (ItemRefAndCodeAddress pending_item :
872          pending_item_refs.item_refs_and_code_addresses) {
873       Address addr;
874       m_process->GetTarget().ResolveLoadAddress(pending_item.code_address,
875                                                 addr);
876       QueueItemSP queue_item_sp(new QueueItem(queue->shared_from_this(),
877                                               m_process->shared_from_this(),
878                                               pending_item.item_ref, addr));
879       queue->PushPendingQueueItem(queue_item_sp);
880     }
881   }
882 }
883 
CompleteQueueItem(QueueItem * queue_item,addr_t item_ref)884 void SystemRuntimeMacOSX::CompleteQueueItem(QueueItem *queue_item,
885                                             addr_t item_ref) {
886   AppleGetItemInfoHandler::GetItemInfoReturnInfo ret;
887 
888   ThreadSP cur_thread_sp(
889       m_process->GetThreadList().GetExpressionExecutionThread());
890   Status error;
891   ret = m_get_item_info_handler.GetItemInfo(*cur_thread_sp.get(), item_ref,
892                                             m_page_to_free, m_page_to_free_size,
893                                             error);
894   m_page_to_free = LLDB_INVALID_ADDRESS;
895   m_page_to_free_size = 0;
896   if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS &&
897       ret.item_buffer_size > 0) {
898     DataBufferHeap data(ret.item_buffer_size, 0);
899     if (m_process->ReadMemory(ret.item_buffer_ptr, data.GetBytes(),
900                               ret.item_buffer_size, error) &&
901         error.Success()) {
902       DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
903                               m_process->GetByteOrder(),
904                               m_process->GetAddressByteSize());
905       ItemInfo item = ExtractItemInfoFromBuffer(extractor);
906       queue_item->SetItemThatEnqueuedThis(item.item_that_enqueued_this);
907       queue_item->SetEnqueueingThreadID(item.enqueuing_thread_id);
908       queue_item->SetEnqueueingQueueID(item.enqueuing_queue_serialnum);
909       queue_item->SetStopID(item.stop_id);
910       queue_item->SetEnqueueingBacktrace(item.enqueuing_callstack);
911       queue_item->SetThreadLabel(item.enqueuing_thread_label);
912       queue_item->SetQueueLabel(item.enqueuing_queue_label);
913       queue_item->SetTargetQueueLabel(item.target_queue_label);
914     }
915     m_page_to_free = ret.item_buffer_ptr;
916     m_page_to_free_size = ret.item_buffer_size;
917   }
918 }
919 
PopulateQueuesUsingLibBTR(lldb::addr_t queues_buffer,uint64_t queues_buffer_size,uint64_t count,lldb_private::QueueList & queue_list)920 void SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR(
921     lldb::addr_t queues_buffer, uint64_t queues_buffer_size, uint64_t count,
922     lldb_private::QueueList &queue_list) {
923   Status error;
924   DataBufferHeap data(queues_buffer_size, 0);
925   Log *log = GetLog(LLDBLog::SystemRuntime);
926   if (m_process->ReadMemory(queues_buffer, data.GetBytes(), queues_buffer_size,
927                             error) == queues_buffer_size &&
928       error.Success()) {
929     // We've read the information out of inferior memory; free it on the next
930     // call we make
931     m_page_to_free = queues_buffer;
932     m_page_to_free_size = queues_buffer_size;
933 
934     DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
935                             m_process->GetByteOrder(),
936                             m_process->GetAddressByteSize());
937     offset_t offset = 0;
938     uint64_t queues_read = 0;
939 
940     // The information about the queues is stored in this format (v1): typedef
941     // struct introspection_dispatch_queue_info_s {
942     //     uint32_t offset_to_next;
943     //     dispatch_queue_t queue;
944     //     uint64_t serialnum;     // queue's serialnum in the process, as
945     //     provided by libdispatch
946     //     uint32_t running_work_items_count;
947     //     uint32_t pending_work_items_count;
948     //
949     //     char data[];     // Starting here, we have variable-length data:
950     //     // char queue_label[];
951     // } introspection_dispatch_queue_info_s;
952 
953     while (queues_read < count && offset < queues_buffer_size) {
954       offset_t start_of_this_item = offset;
955 
956       uint32_t offset_to_next = extractor.GetU32(&offset);
957 
958       offset += 4; // Skip over the 4 bytes of reserved space
959       addr_t queue = extractor.GetAddress(&offset);
960       uint64_t serialnum = extractor.GetU64(&offset);
961       uint32_t running_work_items_count = extractor.GetU32(&offset);
962       uint32_t pending_work_items_count = extractor.GetU32(&offset);
963 
964       // Read the first field of the variable length data
965       offset = start_of_this_item +
966                m_lib_backtrace_recording_info.queue_info_data_offset;
967       const char *queue_label = extractor.GetCStr(&offset);
968       if (queue_label == nullptr)
969         queue_label = "";
970 
971       offset_t start_of_next_item = start_of_this_item + offset_to_next;
972       offset = start_of_next_item;
973 
974       LLDB_LOGF(log,
975                 "SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR added "
976                 "queue with dispatch_queue_t 0x%" PRIx64
977                 ", serial number 0x%" PRIx64
978                 ", running items %d, pending items %d, name '%s'",
979                 queue, serialnum, running_work_items_count,
980                 pending_work_items_count, queue_label);
981 
982       QueueSP queue_sp(
983           new Queue(m_process->shared_from_this(), serialnum, queue_label));
984       queue_sp->SetNumRunningWorkItems(running_work_items_count);
985       queue_sp->SetNumPendingWorkItems(pending_work_items_count);
986       queue_sp->SetLibdispatchQueueAddress(queue);
987       queue_sp->SetKind(GetQueueKind(queue));
988       queue_list.AddQueue(queue_sp);
989       queues_read++;
990     }
991   }
992 }
993 
ExtractItemInfoFromBuffer(lldb_private::DataExtractor & extractor)994 SystemRuntimeMacOSX::ItemInfo SystemRuntimeMacOSX::ExtractItemInfoFromBuffer(
995     lldb_private::DataExtractor &extractor) {
996   ItemInfo item;
997 
998   offset_t offset = 0;
999 
1000   item.item_that_enqueued_this = extractor.GetAddress(&offset);
1001   item.function_or_block = extractor.GetAddress(&offset);
1002   item.enqueuing_thread_id = extractor.GetU64(&offset);
1003   item.enqueuing_queue_serialnum = extractor.GetU64(&offset);
1004   item.target_queue_serialnum = extractor.GetU64(&offset);
1005   item.enqueuing_callstack_frame_count = extractor.GetU32(&offset);
1006   item.stop_id = extractor.GetU32(&offset);
1007 
1008   offset = m_lib_backtrace_recording_info.item_info_data_offset;
1009 
1010   for (uint32_t i = 0; i < item.enqueuing_callstack_frame_count; i++) {
1011     item.enqueuing_callstack.push_back(extractor.GetAddress(&offset));
1012   }
1013   item.enqueuing_thread_label = extractor.GetCStr(&offset);
1014   item.enqueuing_queue_label = extractor.GetCStr(&offset);
1015   item.target_queue_label = extractor.GetCStr(&offset);
1016 
1017   return item;
1018 }
1019 
Initialize()1020 void SystemRuntimeMacOSX::Initialize() {
1021   PluginManager::RegisterPlugin(
1022       GetPluginNameStatic(),
1023       "System runtime plugin for Mac OS X native libraries.", CreateInstance);
1024 }
1025 
Terminate()1026 void SystemRuntimeMacOSX::Terminate() {
1027   PluginManager::UnregisterPlugin(CreateInstance);
1028 }
1029