1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | #include "ThreadSanitizerRuntime.h" |
11 | |
12 | #include "Plugins/Process/Utility/HistoryThread.h" |
13 | #include "lldb/Breakpoint/StoppointCallbackContext.h" |
14 | #include "lldb/Core/Debugger.h" |
15 | #include "lldb/Core/Module.h" |
16 | #include "lldb/Core/PluginInterface.h" |
17 | #include "lldb/Core/PluginManager.h" |
18 | #include "lldb/Core/StreamFile.h" |
19 | #include "lldb/Core/ValueObject.h" |
20 | #include "lldb/Expression/UserExpression.h" |
21 | #include "lldb/Interpreter/CommandReturnObject.h" |
22 | #include "lldb/Symbol/Symbol.h" |
23 | #include "lldb/Symbol/SymbolContext.h" |
24 | #include "lldb/Symbol/Variable.h" |
25 | #include "lldb/Symbol/VariableList.h" |
26 | #include "lldb/Target/InstrumentationRuntimeStopInfo.h" |
27 | #include "lldb/Target/SectionLoadList.h" |
28 | #include "lldb/Target/StopInfo.h" |
29 | #include "lldb/Target/Target.h" |
30 | #include "lldb/Target/Thread.h" |
31 | #include "lldb/Utility/RegularExpression.h" |
32 | #include "lldb/Utility/Stream.h" |
33 | |
34 | using namespace lldb; |
35 | using namespace lldb_private; |
36 | |
37 | lldb::InstrumentationRuntimeSP |
38 | ThreadSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) { |
39 | return InstrumentationRuntimeSP(new ThreadSanitizerRuntime(process_sp)); |
40 | } |
41 | |
42 | void ThreadSanitizerRuntime::Initialize() { |
43 | PluginManager::RegisterPlugin( |
44 | GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.", |
45 | CreateInstance, GetTypeStatic); |
46 | } |
47 | |
48 | void ThreadSanitizerRuntime::Terminate() { |
49 | PluginManager::UnregisterPlugin(CreateInstance); |
50 | } |
51 | |
52 | lldb_private::ConstString ThreadSanitizerRuntime::GetPluginNameStatic() { |
53 | return ConstString("ThreadSanitizer"); |
54 | } |
55 | |
56 | lldb::InstrumentationRuntimeType ThreadSanitizerRuntime::GetTypeStatic() { |
57 | return eInstrumentationRuntimeTypeThreadSanitizer; |
58 | } |
59 | |
60 | ThreadSanitizerRuntime::~ThreadSanitizerRuntime() { Deactivate(); } |
61 | |
62 | static constexpr std::chrono::seconds g_retrieve_data_function_timeout(2); |
63 | |
64 | const char *thread_sanitizer_retrieve_report_data_prefix = R"( |
65 | extern "C" |
66 | { |
67 | void *__tsan_get_current_report(); |
68 | int __tsan_get_report_data(void *report, const char **description, int *count, |
69 | int *stack_count, int *mop_count, int *loc_count, |
70 | int *mutex_count, int *thread_count, |
71 | int *unique_tid_count, void **sleep_trace, |
72 | unsigned long trace_size); |
73 | int __tsan_get_report_stack(void *report, unsigned long idx, void **trace, |
74 | unsigned long trace_size); |
75 | int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr, |
76 | int *size, int *write, int *atomic, void **trace, |
77 | unsigned long trace_size); |
78 | int __tsan_get_report_loc(void *report, unsigned long idx, const char **type, |
79 | void **addr, unsigned long *start, unsigned long *size, int *tid, |
80 | int *fd, int *suppressable, void **trace, |
81 | unsigned long trace_size); |
82 | int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr, |
83 | int *destroyed, void **trace, unsigned long trace_size); |
84 | int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id, |
85 | int *running, const char **name, int *parent_tid, |
86 | void **trace, unsigned long trace_size); |
87 | int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid); |
88 | |
89 | // TODO: dlsym won't work on Windows. |
90 | void *dlsym(void* handle, const char* symbol); |
91 | int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type); |
92 | } |
93 | |
94 | const int REPORT_TRACE_SIZE = 128; |
95 | const int REPORT_ARRAY_SIZE = 4; |
96 | |
97 | struct data { |
98 | void *report; |
99 | const char *description; |
100 | int report_count; |
101 | |
102 | void *sleep_trace[REPORT_TRACE_SIZE]; |
103 | |
104 | int stack_count; |
105 | struct { |
106 | int idx; |
107 | void *trace[REPORT_TRACE_SIZE]; |
108 | } stacks[REPORT_ARRAY_SIZE]; |
109 | |
110 | int mop_count; |
111 | struct { |
112 | int idx; |
113 | int tid; |
114 | int size; |
115 | int write; |
116 | int atomic; |
117 | void *addr; |
118 | void *trace[REPORT_TRACE_SIZE]; |
119 | } mops[REPORT_ARRAY_SIZE]; |
120 | |
121 | int loc_count; |
122 | struct { |
123 | int idx; |
124 | const char *type; |
125 | void *addr; |
126 | unsigned long start; |
127 | unsigned long size; |
128 | int tid; |
129 | int fd; |
130 | int suppressable; |
131 | void *trace[REPORT_TRACE_SIZE]; |
132 | const char *object_type; |
133 | } locs[REPORT_ARRAY_SIZE]; |
134 | |
135 | int mutex_count; |
136 | struct { |
137 | int idx; |
138 | unsigned long mutex_id; |
139 | void *addr; |
140 | int destroyed; |
141 | void *trace[REPORT_TRACE_SIZE]; |
142 | } mutexes[REPORT_ARRAY_SIZE]; |
143 | |
144 | int thread_count; |
145 | struct { |
146 | int idx; |
147 | int tid; |
148 | unsigned long os_id; |
149 | int running; |
150 | const char *name; |
151 | int parent_tid; |
152 | void *trace[REPORT_TRACE_SIZE]; |
153 | } threads[REPORT_ARRAY_SIZE]; |
154 | |
155 | int unique_tid_count; |
156 | struct { |
157 | int idx; |
158 | int tid; |
159 | } unique_tids[REPORT_ARRAY_SIZE]; |
160 | }; |
161 | )"; |
162 | |
163 | const char *thread_sanitizer_retrieve_report_data_command = R"( |
164 | data t = {0}; |
165 | |
166 | ptr__tsan_get_report_loc_object_type = (typeof(ptr__tsan_get_report_loc_object_type))(void *)dlsym((void*)-2 /*RTLD_DEFAULT*/, "__tsan_get_report_loc_object_type"); |
167 | |
168 | t.report = __tsan_get_current_report(); |
169 | __tsan_get_report_data(t.report, &t.description, &t.report_count, &t.stack_count, &t.mop_count, &t.loc_count, &t.mutex_count, &t.thread_count, &t.unique_tid_count, t.sleep_trace, REPORT_TRACE_SIZE); |
170 | |
171 | if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE; |
172 | for (int i = 0; i < t.stack_count; i++) { |
173 | t.stacks[i].idx = i; |
174 | __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE); |
175 | } |
176 | |
177 | if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE; |
178 | for (int i = 0; i < t.mop_count; i++) { |
179 | t.mops[i].idx = i; |
180 | __tsan_get_report_mop(t.report, i, &t.mops[i].tid, &t.mops[i].addr, &t.mops[i].size, &t.mops[i].write, &t.mops[i].atomic, t.mops[i].trace, REPORT_TRACE_SIZE); |
181 | } |
182 | |
183 | if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE; |
184 | for (int i = 0; i < t.loc_count; i++) { |
185 | t.locs[i].idx = i; |
186 | __tsan_get_report_loc(t.report, i, &t.locs[i].type, &t.locs[i].addr, &t.locs[i].start, &t.locs[i].size, &t.locs[i].tid, &t.locs[i].fd, &t.locs[i].suppressable, t.locs[i].trace, REPORT_TRACE_SIZE); |
187 | if (ptr__tsan_get_report_loc_object_type) |
188 | ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type); |
189 | } |
190 | |
191 | if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE; |
192 | for (int i = 0; i < t.mutex_count; i++) { |
193 | t.mutexes[i].idx = i; |
194 | __tsan_get_report_mutex(t.report, i, &t.mutexes[i].mutex_id, &t.mutexes[i].addr, &t.mutexes[i].destroyed, t.mutexes[i].trace, REPORT_TRACE_SIZE); |
195 | } |
196 | |
197 | if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE; |
198 | for (int i = 0; i < t.thread_count; i++) { |
199 | t.threads[i].idx = i; |
200 | __tsan_get_report_thread(t.report, i, &t.threads[i].tid, &t.threads[i].os_id, &t.threads[i].running, &t.threads[i].name, &t.threads[i].parent_tid, t.threads[i].trace, REPORT_TRACE_SIZE); |
201 | } |
202 | |
203 | if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE; |
204 | for (int i = 0; i < t.unique_tid_count; i++) { |
205 | t.unique_tids[i].idx = i; |
206 | __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid); |
207 | } |
208 | |
209 | t; |
210 | )"; |
211 | |
212 | static StructuredData::Array * |
213 | CreateStackTrace(ValueObjectSP o, |
214 | const std::string &trace_item_name = ".trace") { |
215 | StructuredData::Array *trace = new StructuredData::Array(); |
216 | ValueObjectSP trace_value_object = |
217 | o->GetValueForExpressionPath(trace_item_name.c_str()); |
218 | size_t count = trace_value_object->GetNumChildren(); |
219 | for (size_t j = 0; j < count; j++) { |
220 | addr_t trace_addr = |
221 | trace_value_object->GetChildAtIndex(j, true)->GetValueAsUnsigned(0); |
222 | if (trace_addr == 0) |
223 | break; |
224 | trace->AddItem( |
225 | StructuredData::ObjectSP(new StructuredData::Integer(trace_addr))); |
226 | } |
227 | return trace; |
228 | } |
229 | |
230 | static StructuredData::Array *ConvertToStructuredArray( |
231 | ValueObjectSP return_value_sp, const std::string &items_name, |
232 | const std::string &count_name, |
233 | std::function<void(ValueObjectSP o, StructuredData::Dictionary *dict)> const |
234 | &callback) { |
235 | StructuredData::Array *array = new StructuredData::Array(); |
| |
236 | unsigned int count = |
237 | return_value_sp->GetValueForExpressionPath(count_name.c_str()) |
238 | ->GetValueAsUnsigned(0); |
239 | ValueObjectSP objects = |
240 | return_value_sp->GetValueForExpressionPath(items_name.c_str()); |
241 | for (unsigned int i = 0; i < count; i++) { |
| 10 | | Assuming 'i' is >= 'count' | |
|
| 11 | | Loop condition is false. Execution continues on line 249 | |
|
242 | ValueObjectSP o = objects->GetChildAtIndex(i, true); |
243 | StructuredData::Dictionary *dict = new StructuredData::Dictionary(); |
244 | |
245 | callback(o, dict); |
246 | |
247 | array->AddItem(StructuredData::ObjectSP(dict)); |
248 | } |
249 | return array; |
250 | } |
251 | |
252 | static std::string RetrieveString(ValueObjectSP return_value_sp, |
253 | ProcessSP process_sp, |
254 | const std::string &expression_path) { |
255 | addr_t ptr = |
256 | return_value_sp->GetValueForExpressionPath(expression_path.c_str()) |
257 | ->GetValueAsUnsigned(0); |
258 | std::string str; |
259 | Status error; |
260 | process_sp->ReadCStringFromMemory(ptr, str, error); |
261 | return str; |
262 | } |
263 | |
264 | static void |
265 | GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data, |
266 | std::map<uint64_t, user_id_t> &thread_id_map) { |
267 | ConvertToStructuredArray( |
| 8 | | Calling 'ConvertToStructuredArray' | |
|
| 12 | | Returned allocated memory | |
|
268 | data, ".threads", ".thread_count", |
269 | [process_sp, &thread_id_map](ValueObjectSP o, |
270 | StructuredData::Dictionary *dict) { |
271 | uint64_t thread_id = |
272 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0); |
273 | uint64_t thread_os_id = |
274 | o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0); |
275 | user_id_t lldb_user_id = 0; |
276 | |
277 | bool can_update = true; |
278 | ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID( |
279 | thread_os_id, can_update); |
280 | if (lldb_thread) { |
281 | lldb_user_id = lldb_thread->GetIndexID(); |
282 | } else { |
283 | |
284 | |
285 | |
286 | |
287 | |
288 | lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id); |
289 | } |
290 | |
291 | thread_id_map[thread_id] = lldb_user_id; |
292 | }); |
293 | } |
| |
294 | |
295 | static user_id_t Renumber(uint64_t id, |
296 | std::map<uint64_t, user_id_t> &thread_id_map) { |
297 | auto IT = thread_id_map.find(id); |
298 | if (IT == thread_id_map.end()) |
299 | return 0; |
300 | |
301 | return IT->second; |
302 | } |
303 | |
304 | StructuredData::ObjectSP |
305 | ThreadSanitizerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) { |
306 | ProcessSP process_sp = GetProcessSP(); |
307 | if (!process_sp) |
| |
308 | return StructuredData::ObjectSP(); |
309 | |
310 | ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); |
311 | StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); |
312 | |
313 | if (!frame_sp) |
| |
314 | return StructuredData::ObjectSP(); |
315 | |
316 | EvaluateExpressionOptions options; |
317 | options.SetUnwindOnError(true); |
318 | options.SetTryAllThreads(true); |
319 | options.SetStopOthers(true); |
320 | options.SetIgnoreBreakpoints(true); |
321 | options.SetTimeout(g_retrieve_data_function_timeout); |
322 | options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix); |
323 | options.SetAutoApplyFixIts(false); |
324 | options.SetLanguage(eLanguageTypeObjC_plus_plus); |
325 | |
326 | ValueObjectSP main_value; |
327 | ExecutionContext exe_ctx; |
328 | Status eval_error; |
329 | frame_sp->CalculateExecutionContext(exe_ctx); |
330 | ExpressionResults result = UserExpression::Evaluate( |
331 | exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "", |
332 | main_value, eval_error); |
333 | if (result != eExpressionCompleted) { |
| 5 | | Assuming 'result' is equal to eExpressionCompleted | |
|
| |
334 | process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf( |
335 | "Warning: Cannot evaluate ThreadSanitizer expression:\n%s\n", |
336 | eval_error.AsCString()); |
337 | return StructuredData::ObjectSP(); |
338 | } |
339 | |
340 | std::map<uint64_t, user_id_t> thread_id_map; |
341 | GetRenumberedThreadIds(process_sp, main_value, thread_id_map); |
| 7 | | Calling 'GetRenumberedThreadIds' | |
|
342 | |
343 | StructuredData::Dictionary *dict = new StructuredData::Dictionary(); |
344 | dict->AddStringItem("instrumentation_class", "ThreadSanitizer"); |
345 | dict->AddStringItem("issue_type", |
346 | RetrieveString(main_value, process_sp, ".description")); |
347 | dict->AddIntegerItem("report_count", |
348 | main_value->GetValueForExpressionPath(".report_count") |
349 | ->GetValueAsUnsigned(0)); |
350 | dict->AddItem("sleep_trace", StructuredData::ObjectSP(CreateStackTrace( |
351 | main_value, ".sleep_trace"))); |
352 | |
353 | StructuredData::Array *stacks = ConvertToStructuredArray( |
354 | main_value, ".stacks", ".stack_count", |
355 | [thread_sp](ValueObjectSP o, StructuredData::Dictionary *dict) { |
356 | dict->AddIntegerItem( |
357 | "index", |
358 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
359 | dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); |
360 | |
361 | dict->AddIntegerItem("thread_id", thread_sp->GetIndexID()); |
362 | }); |
363 | dict->AddItem("stacks", StructuredData::ObjectSP(stacks)); |
364 | |
365 | StructuredData::Array *mops = ConvertToStructuredArray( |
366 | main_value, ".mops", ".mop_count", |
367 | [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { |
368 | dict->AddIntegerItem( |
369 | "index", |
370 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
371 | dict->AddIntegerItem( |
372 | "thread_id", |
373 | Renumber( |
374 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
375 | thread_id_map)); |
376 | dict->AddIntegerItem( |
377 | "size", |
378 | o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); |
379 | dict->AddBooleanItem( |
380 | "is_write", |
381 | o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0)); |
382 | dict->AddBooleanItem( |
383 | "is_atomic", |
384 | o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0)); |
385 | dict->AddIntegerItem( |
386 | "address", |
387 | o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); |
388 | dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); |
389 | }); |
390 | dict->AddItem("mops", StructuredData::ObjectSP(mops)); |
391 | |
392 | StructuredData::Array *locs = ConvertToStructuredArray( |
393 | main_value, ".locs", ".loc_count", |
394 | [process_sp, &thread_id_map](ValueObjectSP o, |
395 | StructuredData::Dictionary *dict) { |
396 | dict->AddIntegerItem( |
397 | "index", |
398 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
399 | dict->AddStringItem("type", RetrieveString(o, process_sp, ".type")); |
400 | dict->AddIntegerItem( |
401 | "address", |
402 | o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); |
403 | dict->AddIntegerItem( |
404 | "start", |
405 | o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0)); |
406 | dict->AddIntegerItem( |
407 | "size", |
408 | o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); |
409 | dict->AddIntegerItem( |
410 | "thread_id", |
411 | Renumber( |
412 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
413 | thread_id_map)); |
414 | dict->AddIntegerItem( |
415 | "file_descriptor", |
416 | o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0)); |
417 | dict->AddIntegerItem("suppressable", |
418 | o->GetValueForExpressionPath(".suppressable") |
419 | ->GetValueAsUnsigned(0)); |
420 | dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); |
421 | dict->AddStringItem("object_type", |
422 | RetrieveString(o, process_sp, ".object_type")); |
423 | }); |
424 | dict->AddItem("locs", StructuredData::ObjectSP(locs)); |
425 | |
426 | StructuredData::Array *mutexes = ConvertToStructuredArray( |
427 | main_value, ".mutexes", ".mutex_count", |
428 | [](ValueObjectSP o, StructuredData::Dictionary *dict) { |
429 | dict->AddIntegerItem( |
430 | "index", |
431 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
432 | dict->AddIntegerItem( |
433 | "mutex_id", |
434 | o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0)); |
435 | dict->AddIntegerItem( |
436 | "address", |
437 | o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); |
438 | dict->AddIntegerItem( |
439 | "destroyed", |
440 | o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0)); |
441 | dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); |
442 | }); |
443 | dict->AddItem("mutexes", StructuredData::ObjectSP(mutexes)); |
444 | |
445 | StructuredData::Array *threads = ConvertToStructuredArray( |
446 | main_value, ".threads", ".thread_count", |
447 | [process_sp, &thread_id_map](ValueObjectSP o, |
448 | StructuredData::Dictionary *dict) { |
449 | dict->AddIntegerItem( |
450 | "index", |
451 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
452 | dict->AddIntegerItem( |
453 | "thread_id", |
454 | Renumber( |
455 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
456 | thread_id_map)); |
457 | dict->AddIntegerItem( |
458 | "thread_os_id", |
459 | o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0)); |
460 | dict->AddIntegerItem( |
461 | "running", |
462 | o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0)); |
463 | dict->AddStringItem("name", RetrieveString(o, process_sp, ".name")); |
464 | dict->AddIntegerItem( |
465 | "parent_thread_id", |
466 | Renumber(o->GetValueForExpressionPath(".parent_tid") |
467 | ->GetValueAsUnsigned(0), |
468 | thread_id_map)); |
469 | dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); |
470 | }); |
471 | dict->AddItem("threads", StructuredData::ObjectSP(threads)); |
472 | |
473 | StructuredData::Array *unique_tids = ConvertToStructuredArray( |
474 | main_value, ".unique_tids", ".unique_tid_count", |
475 | [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { |
476 | dict->AddIntegerItem( |
477 | "index", |
478 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
479 | dict->AddIntegerItem( |
480 | "tid", |
481 | Renumber( |
482 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
483 | thread_id_map)); |
484 | }); |
485 | dict->AddItem("unique_tids", StructuredData::ObjectSP(unique_tids)); |
486 | |
487 | return StructuredData::ObjectSP(dict); |
488 | } |
489 | |
490 | std::string |
491 | ThreadSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) { |
492 | std::string description = report->GetAsDictionary() |
493 | ->GetValueForKey("issue_type") |
494 | ->GetAsString() |
495 | ->GetValue(); |
496 | |
497 | if (description == "data-race") { |
498 | return "Data race"; |
499 | } else if (description == "data-race-vptr") { |
500 | return "Data race on C++ virtual pointer"; |
501 | } else if (description == "heap-use-after-free") { |
502 | return "Use of deallocated memory"; |
503 | } else if (description == "heap-use-after-free-vptr") { |
504 | return "Use of deallocated C++ virtual pointer"; |
505 | } else if (description == "thread-leak") { |
506 | return "Thread leak"; |
507 | } else if (description == "locked-mutex-destroy") { |
508 | return "Destruction of a locked mutex"; |
509 | } else if (description == "mutex-double-lock") { |
510 | return "Double lock of a mutex"; |
511 | } else if (description == "mutex-invalid-access") { |
512 | return "Use of an uninitialized or destroyed mutex"; |
513 | } else if (description == "mutex-bad-unlock") { |
514 | return "Unlock of an unlocked mutex (or by a wrong thread)"; |
515 | } else if (description == "mutex-bad-read-lock") { |
516 | return "Read lock of a write locked mutex"; |
517 | } else if (description == "mutex-bad-read-unlock") { |
518 | return "Read unlock of a write locked mutex"; |
519 | } else if (description == "signal-unsafe-call") { |
520 | return "Signal-unsafe call inside a signal handler"; |
521 | } else if (description == "errno-in-signal-handler") { |
522 | return "Overwrite of errno in a signal handler"; |
523 | } else if (description == "lock-order-inversion") { |
524 | return "Lock order inversion (potential deadlock)"; |
525 | } else if (description == "external-race") { |
526 | return "Race on a library object"; |
527 | } else if (description == "swift-access-race") { |
528 | return "Swift access race"; |
529 | } |
530 | |
531 | |
532 | return description; |
533 | } |
534 | |
535 | static std::string Sprintf(const char *format, ...) { |
536 | StreamString s; |
537 | va_list args; |
538 | va_start(args, format)__builtin_va_start(args, format); |
539 | s.PrintfVarArg(format, args); |
540 | va_end(args)__builtin_va_end(args); |
541 | return s.GetString(); |
542 | } |
543 | |
544 | static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) { |
545 | lldb_private::Address so_addr; |
546 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, |
547 | so_addr)) |
548 | return ""; |
549 | |
550 | lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); |
551 | if (!symbol) |
552 | return ""; |
553 | |
554 | std::string sym_name = symbol->GetName().GetCString(); |
555 | return sym_name; |
556 | } |
557 | |
558 | static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr, |
559 | Declaration &decl) { |
560 | lldb_private::Address so_addr; |
561 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, |
562 | so_addr)) |
563 | return; |
564 | |
565 | lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); |
566 | if (!symbol) |
567 | return; |
568 | |
569 | ConstString sym_name = symbol->GetMangled().GetName( |
570 | lldb::eLanguageTypeUnknown, Mangled::ePreferMangled); |
571 | |
572 | ModuleSP module = symbol->CalculateSymbolContextModule(); |
573 | if (!module) |
574 | return; |
575 | |
576 | VariableList var_list; |
577 | module->FindGlobalVariables(sym_name, nullptr, true, 1U, var_list); |
578 | if (var_list.GetSize() < 1) |
579 | return; |
580 | |
581 | VariableSP var = var_list.GetVariableAtIndex(0); |
582 | decl = var->GetDeclaration(); |
583 | } |
584 | |
585 | addr_t ThreadSanitizerRuntime::GetFirstNonInternalFramePc( |
586 | StructuredData::ObjectSP trace, bool skip_one_frame) { |
587 | ProcessSP process_sp = GetProcessSP(); |
588 | ModuleSP runtime_module_sp = GetRuntimeModuleSP(); |
589 | |
590 | StructuredData::Array *trace_array = trace->GetAsArray(); |
591 | for (size_t i = 0; i < trace_array->GetSize(); i++) { |
592 | if (skip_one_frame && i == 0) |
593 | continue; |
594 | |
595 | addr_t addr; |
596 | if (!trace_array->GetItemAtIndexAsInteger(i, addr)) |
597 | continue; |
598 | |
599 | lldb_private::Address so_addr; |
600 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress( |
601 | addr, so_addr)) |
602 | continue; |
603 | |
604 | if (so_addr.GetModule() == runtime_module_sp) |
605 | continue; |
606 | |
607 | return addr; |
608 | } |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | std::string |
614 | ThreadSanitizerRuntime::GenerateSummary(StructuredData::ObjectSP report) { |
615 | ProcessSP process_sp = GetProcessSP(); |
616 | |
617 | std::string summary = report->GetAsDictionary() |
618 | ->GetValueForKey("description") |
619 | ->GetAsString() |
620 | ->GetValue(); |
621 | bool skip_one_frame = |
622 | report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() == |
623 | "external-race"; |
624 | |
625 | addr_t pc = 0; |
626 | if (report->GetAsDictionary() |
627 | ->GetValueForKey("mops") |
628 | ->GetAsArray() |
629 | ->GetSize() > 0) |
630 | pc = GetFirstNonInternalFramePc(report->GetAsDictionary() |
631 | ->GetValueForKey("mops") |
632 | ->GetAsArray() |
633 | ->GetItemAtIndex(0) |
634 | ->GetAsDictionary() |
635 | ->GetValueForKey("trace"), |
636 | skip_one_frame); |
637 | |
638 | if (report->GetAsDictionary() |
639 | ->GetValueForKey("stacks") |
640 | ->GetAsArray() |
641 | ->GetSize() > 0) |
642 | pc = GetFirstNonInternalFramePc(report->GetAsDictionary() |
643 | ->GetValueForKey("stacks") |
644 | ->GetAsArray() |
645 | ->GetItemAtIndex(0) |
646 | ->GetAsDictionary() |
647 | ->GetValueForKey("trace"), |
648 | skip_one_frame); |
649 | |
650 | if (pc != 0) { |
651 | summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc); |
652 | } |
653 | |
654 | if (report->GetAsDictionary() |
655 | ->GetValueForKey("locs") |
656 | ->GetAsArray() |
657 | ->GetSize() > 0) { |
658 | StructuredData::ObjectSP loc = report->GetAsDictionary() |
659 | ->GetValueForKey("locs") |
660 | ->GetAsArray() |
661 | ->GetItemAtIndex(0); |
662 | std::string object_type = loc->GetAsDictionary() |
663 | ->GetValueForKey("object_type") |
664 | ->GetAsString() |
665 | ->GetValue(); |
666 | if (!object_type.empty()) { |
667 | summary = "Race on " + object_type + " object"; |
668 | } |
669 | addr_t addr = loc->GetAsDictionary() |
670 | ->GetValueForKey("address") |
671 | ->GetAsInteger() |
672 | ->GetValue(); |
673 | if (addr == 0) |
674 | addr = loc->GetAsDictionary() |
675 | ->GetValueForKey("start") |
676 | ->GetAsInteger() |
677 | ->GetValue(); |
678 | |
679 | if (addr != 0) { |
680 | std::string global_name = GetSymbolNameFromAddress(process_sp, addr); |
681 | if (!global_name.empty()) { |
682 | summary = summary + " at " + global_name; |
683 | } else { |
684 | summary = summary + " at " + Sprintf("0x%llx", addr); |
685 | } |
686 | } else { |
687 | int fd = loc->GetAsDictionary() |
688 | ->GetValueForKey("file_descriptor") |
689 | ->GetAsInteger() |
690 | ->GetValue(); |
691 | if (fd != 0) { |
692 | summary = summary + " on file descriptor " + Sprintf("%d", fd); |
693 | } |
694 | } |
695 | } |
696 | |
697 | return summary; |
698 | } |
699 | |
700 | addr_t |
701 | ThreadSanitizerRuntime::GetMainRacyAddress(StructuredData::ObjectSP report) { |
702 | addr_t result = (addr_t)-1; |
703 | |
704 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( |
705 | [&result](StructuredData::Object *o) -> bool { |
706 | addr_t addr = |
707 | o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); |
708 | if (addr < result) |
709 | result = addr; |
710 | return true; |
711 | }); |
712 | |
713 | return (result == (addr_t)-1) ? 0 : result; |
714 | } |
715 | |
716 | std::string ThreadSanitizerRuntime::GetLocationDescription( |
717 | StructuredData::ObjectSP report, addr_t &global_addr, |
718 | std::string &global_name, std::string &filename, uint32_t &line) { |
719 | std::string result = ""; |
720 | |
721 | ProcessSP process_sp = GetProcessSP(); |
722 | |
723 | if (report->GetAsDictionary() |
724 | ->GetValueForKey("locs") |
725 | ->GetAsArray() |
726 | ->GetSize() > 0) { |
727 | StructuredData::ObjectSP loc = report->GetAsDictionary() |
728 | ->GetValueForKey("locs") |
729 | ->GetAsArray() |
730 | ->GetItemAtIndex(0); |
731 | std::string type = |
732 | loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); |
733 | if (type == "global") { |
734 | global_addr = loc->GetAsDictionary() |
735 | ->GetValueForKey("address") |
736 | ->GetAsInteger() |
737 | ->GetValue(); |
738 | global_name = GetSymbolNameFromAddress(process_sp, global_addr); |
739 | if (!global_name.empty()) { |
740 | result = Sprintf("'%s' is a global variable (0x%llx)", |
741 | global_name.c_str(), global_addr); |
742 | } else { |
743 | result = Sprintf("0x%llx is a global variable", global_addr); |
744 | } |
745 | |
746 | Declaration decl; |
747 | GetSymbolDeclarationFromAddress(process_sp, global_addr, decl); |
748 | if (decl.GetFile()) { |
749 | filename = decl.GetFile().GetPath(); |
750 | line = decl.GetLine(); |
751 | } |
752 | } else if (type == "heap") { |
753 | addr_t addr = loc->GetAsDictionary() |
754 | ->GetValueForKey("start") |
755 | ->GetAsInteger() |
756 | ->GetValue(); |
757 | long size = loc->GetAsDictionary() |
758 | ->GetValueForKey("size") |
759 | ->GetAsInteger() |
760 | ->GetValue(); |
761 | std::string object_type = loc->GetAsDictionary() |
762 | ->GetValueForKey("object_type") |
763 | ->GetAsString() |
764 | ->GetValue(); |
765 | if (!object_type.empty()) { |
766 | result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size, |
767 | object_type.c_str(), addr); |
768 | } else { |
769 | result = |
770 | Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); |
771 | } |
772 | } else if (type == "stack") { |
773 | int tid = loc->GetAsDictionary() |
774 | ->GetValueForKey("thread_id") |
775 | ->GetAsInteger() |
776 | ->GetValue(); |
777 | result = Sprintf("Location is stack of thread %d", tid); |
778 | } else if (type == "tls") { |
779 | int tid = loc->GetAsDictionary() |
780 | ->GetValueForKey("thread_id") |
781 | ->GetAsInteger() |
782 | ->GetValue(); |
783 | result = Sprintf("Location is TLS of thread %d", tid); |
784 | } else if (type == "fd") { |
785 | int fd = loc->GetAsDictionary() |
786 | ->GetValueForKey("file_descriptor") |
787 | ->GetAsInteger() |
788 | ->GetValue(); |
789 | result = Sprintf("Location is file descriptor %d", fd); |
790 | } |
791 | } |
792 | |
793 | return result; |
794 | } |
795 | |
796 | bool ThreadSanitizerRuntime::NotifyBreakpointHit( |
797 | void *baton, StoppointCallbackContext *context, user_id_t break_id, |
798 | user_id_t break_loc_id) { |
799 | assert(baton && "null baton")((baton && "null baton") ? static_cast<void> (0 ) : __assert_fail ("baton && \"null baton\"", "/tmp/buildd/llvm-toolchain-snapshot-5.0~svn303373/tools/lldb/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp" , 799, __PRETTY_FUNCTION__)); |
800 | if (!baton) |
| |
801 | return false; |
802 | |
803 | ThreadSanitizerRuntime *const instance = |
804 | static_cast<ThreadSanitizerRuntime *>(baton); |
805 | |
806 | StructuredData::ObjectSP report = |
807 | instance->RetrieveReportData(context->exe_ctx_ref); |
| 2 | | Calling 'ThreadSanitizerRuntime::RetrieveReportData' | |
|
808 | std::string stop_reason_description; |
809 | if (report) { |
810 | std::string issue_description = instance->FormatDescription(report); |
811 | report->GetAsDictionary()->AddStringItem("description", issue_description); |
812 | stop_reason_description = issue_description + " detected"; |
813 | report->GetAsDictionary()->AddStringItem("stop_description", |
814 | stop_reason_description); |
815 | std::string summary = instance->GenerateSummary(report); |
816 | report->GetAsDictionary()->AddStringItem("summary", summary); |
817 | addr_t main_address = instance->GetMainRacyAddress(report); |
818 | report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); |
819 | |
820 | addr_t global_addr = 0; |
821 | std::string global_name = ""; |
822 | std::string location_filename = ""; |
823 | uint32_t location_line = 0; |
824 | std::string location_description = instance->GetLocationDescription( |
825 | report, global_addr, global_name, location_filename, location_line); |
826 | report->GetAsDictionary()->AddStringItem("location_description", |
827 | location_description); |
828 | if (global_addr != 0) { |
829 | report->GetAsDictionary()->AddIntegerItem("global_address", global_addr); |
830 | } |
831 | if (!global_name.empty()) { |
832 | report->GetAsDictionary()->AddStringItem("global_name", global_name); |
833 | } |
834 | if (location_filename != "") { |
835 | report->GetAsDictionary()->AddStringItem("location_filename", |
836 | location_filename); |
837 | report->GetAsDictionary()->AddIntegerItem("location_line", location_line); |
838 | } |
839 | |
840 | bool all_addresses_are_same = true; |
841 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( |
842 | [&all_addresses_are_same, |
843 | main_address](StructuredData::Object *o) -> bool { |
844 | addr_t addr = |
845 | o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); |
846 | if (main_address != addr) |
847 | all_addresses_are_same = false; |
848 | return true; |
849 | }); |
850 | report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same", |
851 | all_addresses_are_same); |
852 | } |
853 | |
854 | ProcessSP process_sp = instance->GetProcessSP(); |
855 | |
856 | if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { |
857 | ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); |
858 | if (thread_sp) |
859 | thread_sp->SetStopInfo( |
860 | InstrumentationRuntimeStopInfo:: |
861 | CreateStopReasonWithInstrumentationData( |
862 | *thread_sp, stop_reason_description, report)); |
863 | |
864 | StreamFileSP stream_sp( |
865 | process_sp->GetTarget().GetDebugger().GetOutputFile()); |
866 | if (stream_sp) { |
867 | stream_sp->Printf("ThreadSanitizer report breakpoint hit. Use 'thread " |
868 | "info -s' to get extended information about the " |
869 | "report.\n"); |
870 | } |
871 | return true; |
872 | } else |
873 | return false; |
874 | } |
875 | |
876 | const RegularExpression &ThreadSanitizerRuntime::GetPatternForRuntimeLibrary() { |
877 | static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_")); |
878 | return regex; |
879 | } |
880 | |
881 | bool ThreadSanitizerRuntime::CheckIfRuntimeIsValid( |
882 | const lldb::ModuleSP module_sp) { |
883 | static ConstString g_tsan_get_current_report("__tsan_get_current_report"); |
884 | const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( |
885 | g_tsan_get_current_report, lldb::eSymbolTypeAny); |
886 | return symbol != nullptr; |
887 | } |
888 | |
889 | void ThreadSanitizerRuntime::Activate() { |
890 | if (IsActive()) |
891 | return; |
892 | |
893 | ProcessSP process_sp = GetProcessSP(); |
894 | if (!process_sp) |
895 | return; |
896 | |
897 | ConstString symbol_name("__tsan_on_report"); |
898 | const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType( |
899 | symbol_name, eSymbolTypeCode); |
900 | |
901 | if (symbol == NULL__null) |
902 | return; |
903 | |
904 | if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) |
905 | return; |
906 | |
907 | Target &target = process_sp->GetTarget(); |
908 | addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); |
909 | |
910 | if (symbol_address == LLDB_INVALID_ADDRESS(18446744073709551615UL)) |
911 | return; |
912 | |
913 | bool internal = true; |
914 | bool hardware = false; |
915 | Breakpoint *breakpoint = |
916 | process_sp->GetTarget() |
917 | .CreateBreakpoint(symbol_address, internal, hardware) |
918 | .get(); |
919 | breakpoint->SetCallback(ThreadSanitizerRuntime::NotifyBreakpointHit, this, |
920 | true); |
921 | breakpoint->SetBreakpointKind("thread-sanitizer-report"); |
922 | SetBreakpointID(breakpoint->GetID()); |
923 | |
924 | StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile()); |
925 | if (stream_sp) { |
926 | stream_sp->Printf("ThreadSanitizer debugger support is active.\n"); |
927 | } |
928 | |
929 | SetActive(true); |
930 | } |
931 | |
932 | void ThreadSanitizerRuntime::Deactivate() { |
933 | if (GetBreakpointID() != LLDB_INVALID_BREAK_ID0) { |
934 | ProcessSP process_sp = GetProcessSP(); |
935 | if (process_sp) { |
936 | process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); |
937 | SetBreakpointID(LLDB_INVALID_BREAK_ID0); |
938 | } |
939 | } |
940 | SetActive(false); |
941 | } |
942 | static std::string GenerateThreadName(const std::string &path, |
943 | StructuredData::Object *o, |
944 | StructuredData::ObjectSP main_info) { |
945 | std::string result = "additional information"; |
946 | |
947 | if (path == "mops") { |
948 | int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue(); |
949 | int thread_id = |
950 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); |
951 | bool is_write = |
952 | o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); |
953 | bool is_atomic = |
954 | o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue(); |
955 | addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); |
956 | |
957 | std::string addr_string = Sprintf(" at 0x%llx", addr); |
958 | |
959 | if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same") |
960 | ->GetBooleanValue()) { |
961 | addr_string = ""; |
962 | } |
963 | |
964 | if (main_info->GetObjectForDotSeparatedPath("issue_type") |
965 | ->GetStringValue() == "external-race") { |
966 | result = Sprintf("%s access by thread %d", |
967 | is_write ? "mutating" : "read-only", thread_id); |
968 | } else if (main_info->GetObjectForDotSeparatedPath("issue_type") |
969 | ->GetStringValue() == "swift-access-race") { |
970 | result = Sprintf("modifying access by thread %d", thread_id); |
971 | } else { |
972 | result = Sprintf("%s%s of size %d%s by thread %d", |
973 | is_atomic ? "atomic " : "", is_write ? "write" : "read", |
974 | size, addr_string.c_str(), thread_id); |
975 | } |
976 | } |
977 | |
978 | if (path == "threads") { |
979 | int thread_id = |
980 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); |
981 | result = Sprintf("Thread %d created", thread_id); |
982 | } |
983 | |
984 | if (path == "locs") { |
985 | std::string type = |
986 | o->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); |
987 | int thread_id = |
988 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); |
989 | int fd = |
990 | o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue(); |
991 | if (type == "heap") { |
992 | result = Sprintf("Heap block allocated by thread %d", thread_id); |
993 | } else if (type == "fd") { |
994 | result = |
995 | Sprintf("File descriptor %d created by thread %t", fd, thread_id); |
996 | } |
997 | } |
998 | |
999 | if (path == "mutexes") { |
1000 | int mutex_id = |
1001 | o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue(); |
1002 | |
1003 | result = Sprintf("Mutex M%d created", mutex_id); |
1004 | } |
1005 | |
1006 | if (path == "stacks") { |
1007 | int thread_id = |
1008 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); |
1009 | result = Sprintf("Thread %d", thread_id); |
1010 | } |
1011 | |
1012 | result[0] = toupper(result[0]); |
1013 | |
1014 | return result; |
1015 | } |
1016 | |
1017 | static void AddThreadsForPath(const std::string &path, |
1018 | ThreadCollectionSP threads, ProcessSP process_sp, |
1019 | StructuredData::ObjectSP info) { |
1020 | info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach( |
1021 | [process_sp, threads, path, info](StructuredData::Object *o) -> bool { |
1022 | std::vector<lldb::addr_t> pcs; |
1023 | o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach( |
1024 | [&pcs](StructuredData::Object *pc) -> bool { |
1025 | pcs.push_back(pc->GetAsInteger()->GetValue()); |
1026 | return true; |
1027 | }); |
1028 | |
1029 | if (pcs.size() == 0) |
1030 | return true; |
1031 | |
1032 | StructuredData::ObjectSP thread_id_obj = |
1033 | o->GetObjectForDotSeparatedPath("thread_os_id"); |
1034 | tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; |
1035 | |
1036 | uint32_t stop_id = 0; |
1037 | bool stop_id_is_valid = false; |
1038 | HistoryThread *history_thread = |
1039 | new HistoryThread(*process_sp, tid, pcs, stop_id, stop_id_is_valid); |
1040 | ThreadSP new_thread_sp(history_thread); |
1041 | new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str()); |
1042 | |
1043 | |
1044 | |
1045 | process_sp->GetExtendedThreadList().AddThread(new_thread_sp); |
1046 | threads->AddThread(new_thread_sp); |
1047 | |
1048 | return true; |
1049 | }); |
1050 | } |
1051 | |
1052 | lldb::ThreadCollectionSP |
1053 | ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo( |
1054 | StructuredData::ObjectSP info) { |
1055 | ThreadCollectionSP threads; |
1056 | threads.reset(new ThreadCollection()); |
1057 | |
1058 | if (info->GetObjectForDotSeparatedPath("instrumentation_class") |
1059 | ->GetStringValue() != "ThreadSanitizer") |
1060 | return threads; |
1061 | |
1062 | ProcessSP process_sp = GetProcessSP(); |
1063 | |
1064 | AddThreadsForPath("stacks", threads, process_sp, info); |
1065 | AddThreadsForPath("mops", threads, process_sp, info); |
1066 | AddThreadsForPath("locs", threads, process_sp, info); |
1067 | AddThreadsForPath("mutexes", threads, process_sp, info); |
1068 | AddThreadsForPath("threads", threads, process_sp, info); |
1069 | |
1070 | return threads; |
1071 | } |