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