File: | src/gnu/usr.bin/clang/liblldbPluginInstrumentationRuntime/../../../llvm/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp |
Warning: | line 293, column 1 Potential memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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/StreamFile.h" | |||
18 | #include "lldb/Core/ValueObject.h" | |||
19 | #include "lldb/Expression/UserExpression.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/RegularExpression.h" | |||
31 | #include "lldb/Utility/Stream.h" | |||
32 | ||||
33 | #include <memory> | |||
34 | ||||
35 | using namespace lldb; | |||
36 | using namespace lldb_private; | |||
37 | ||||
38 | LLDB_PLUGIN_DEFINE(InstrumentationRuntimeTSan)namespace lldb_private { void lldb_initialize_InstrumentationRuntimeTSan () { InstrumentationRuntimeTSan::Initialize(); } void lldb_terminate_InstrumentationRuntimeTSan () { InstrumentationRuntimeTSan::Terminate(); } } | |||
39 | ||||
40 | lldb::InstrumentationRuntimeSP | |||
41 | InstrumentationRuntimeTSan::CreateInstance(const lldb::ProcessSP &process_sp) { | |||
42 | return InstrumentationRuntimeSP(new InstrumentationRuntimeTSan(process_sp)); | |||
43 | } | |||
44 | ||||
45 | void InstrumentationRuntimeTSan::Initialize() { | |||
46 | PluginManager::RegisterPlugin( | |||
47 | GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.", | |||
48 | CreateInstance, GetTypeStatic); | |||
49 | } | |||
50 | ||||
51 | void InstrumentationRuntimeTSan::Terminate() { | |||
52 | PluginManager::UnregisterPlugin(CreateInstance); | |||
53 | } | |||
54 | ||||
55 | lldb_private::ConstString InstrumentationRuntimeTSan::GetPluginNameStatic() { | |||
56 | return ConstString("ThreadSanitizer"); | |||
57 | } | |||
58 | ||||
59 | lldb::InstrumentationRuntimeType InstrumentationRuntimeTSan::GetTypeStatic() { | |||
60 | return eInstrumentationRuntimeTypeThreadSanitizer; | |||
61 | } | |||
62 | ||||
63 | InstrumentationRuntimeTSan::~InstrumentationRuntimeTSan() { Deactivate(); } | |||
64 | ||||
65 | const char *thread_sanitizer_retrieve_report_data_prefix = R"( | |||
66 | extern "C" | |||
67 | { | |||
68 | void *__tsan_get_current_report(); | |||
69 | int __tsan_get_report_data(void *report, const char **description, int *count, | |||
70 | int *stack_count, int *mop_count, int *loc_count, | |||
71 | int *mutex_count, int *thread_count, | |||
72 | int *unique_tid_count, void **sleep_trace, | |||
73 | unsigned long trace_size); | |||
74 | int __tsan_get_report_stack(void *report, unsigned long idx, void **trace, | |||
75 | unsigned long trace_size); | |||
76 | int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr, | |||
77 | int *size, int *write, int *atomic, void **trace, | |||
78 | unsigned long trace_size); | |||
79 | int __tsan_get_report_loc(void *report, unsigned long idx, const char **type, | |||
80 | void **addr, unsigned long *start, unsigned long *size, int *tid, | |||
81 | int *fd, int *suppressable, void **trace, | |||
82 | unsigned long trace_size); | |||
83 | int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr, | |||
84 | int *destroyed, void **trace, unsigned long trace_size); | |||
85 | int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id, | |||
86 | int *running, const char **name, int *parent_tid, | |||
87 | void **trace, unsigned long trace_size); | |||
88 | int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid); | |||
89 | ||||
90 | // TODO: dlsym won't work on Windows. | |||
91 | void *dlsym(void* handle, const char* symbol); | |||
92 | int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type); | |||
93 | } | |||
94 | ||||
95 | const int REPORT_TRACE_SIZE = 128; | |||
96 | const int REPORT_ARRAY_SIZE = 4; | |||
97 | ||||
98 | struct data { | |||
99 | void *report; | |||
100 | const char *description; | |||
101 | int report_count; | |||
102 | ||||
103 | void *sleep_trace[REPORT_TRACE_SIZE]; | |||
104 | ||||
105 | int stack_count; | |||
106 | struct { | |||
107 | int idx; | |||
108 | void *trace[REPORT_TRACE_SIZE]; | |||
109 | } stacks[REPORT_ARRAY_SIZE]; | |||
110 | ||||
111 | int mop_count; | |||
112 | struct { | |||
113 | int idx; | |||
114 | int tid; | |||
115 | int size; | |||
116 | int write; | |||
117 | int atomic; | |||
118 | void *addr; | |||
119 | void *trace[REPORT_TRACE_SIZE]; | |||
120 | } mops[REPORT_ARRAY_SIZE]; | |||
121 | ||||
122 | int loc_count; | |||
123 | struct { | |||
124 | int idx; | |||
125 | const char *type; | |||
126 | void *addr; | |||
127 | unsigned long start; | |||
128 | unsigned long size; | |||
129 | int tid; | |||
130 | int fd; | |||
131 | int suppressable; | |||
132 | void *trace[REPORT_TRACE_SIZE]; | |||
133 | const char *object_type; | |||
134 | } locs[REPORT_ARRAY_SIZE]; | |||
135 | ||||
136 | int mutex_count; | |||
137 | struct { | |||
138 | int idx; | |||
139 | unsigned long mutex_id; | |||
140 | void *addr; | |||
141 | int destroyed; | |||
142 | void *trace[REPORT_TRACE_SIZE]; | |||
143 | } mutexes[REPORT_ARRAY_SIZE]; | |||
144 | ||||
145 | int thread_count; | |||
146 | struct { | |||
147 | int idx; | |||
148 | int tid; | |||
149 | unsigned long os_id; | |||
150 | int running; | |||
151 | const char *name; | |||
152 | int parent_tid; | |||
153 | void *trace[REPORT_TRACE_SIZE]; | |||
154 | } threads[REPORT_ARRAY_SIZE]; | |||
155 | ||||
156 | int unique_tid_count; | |||
157 | struct { | |||
158 | int idx; | |||
159 | int tid; | |||
160 | } unique_tids[REPORT_ARRAY_SIZE]; | |||
161 | }; | |||
162 | )"; | |||
163 | ||||
164 | const char *thread_sanitizer_retrieve_report_data_command = R"( | |||
165 | data t = {0}; | |||
166 | ||||
167 | 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"); | |||
168 | ||||
169 | t.report = __tsan_get_current_report(); | |||
170 | __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); | |||
171 | ||||
172 | if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE; | |||
173 | for (int i = 0; i < t.stack_count; i++) { | |||
174 | t.stacks[i].idx = i; | |||
175 | __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE); | |||
176 | } | |||
177 | ||||
178 | if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE; | |||
179 | for (int i = 0; i < t.mop_count; i++) { | |||
180 | t.mops[i].idx = i; | |||
181 | __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); | |||
182 | } | |||
183 | ||||
184 | if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE; | |||
185 | for (int i = 0; i < t.loc_count; i++) { | |||
186 | t.locs[i].idx = i; | |||
187 | __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); | |||
188 | if (ptr__tsan_get_report_loc_object_type) | |||
189 | ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type); | |||
190 | } | |||
191 | ||||
192 | if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE; | |||
193 | for (int i = 0; i < t.mutex_count; i++) { | |||
194 | t.mutexes[i].idx = i; | |||
195 | __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); | |||
196 | } | |||
197 | ||||
198 | if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE; | |||
199 | for (int i = 0; i < t.thread_count; i++) { | |||
200 | t.threads[i].idx = i; | |||
201 | __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); | |||
202 | } | |||
203 | ||||
204 | if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE; | |||
205 | for (int i = 0; i < t.unique_tid_count; i++) { | |||
206 | t.unique_tids[i].idx = i; | |||
207 | __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid); | |||
208 | } | |||
209 | ||||
210 | t; | |||
211 | )"; | |||
212 | ||||
213 | static StructuredData::Array * | |||
214 | CreateStackTrace(ValueObjectSP o, | |||
215 | const std::string &trace_item_name = ".trace") { | |||
216 | StructuredData::Array *trace = new StructuredData::Array(); | |||
217 | ValueObjectSP trace_value_object = | |||
218 | o->GetValueForExpressionPath(trace_item_name.c_str()); | |||
219 | size_t count = trace_value_object->GetNumChildren(); | |||
220 | for (size_t j = 0; j < count; j++) { | |||
221 | addr_t trace_addr = | |||
222 | trace_value_object->GetChildAtIndex(j, true)->GetValueAsUnsigned(0); | |||
223 | if (trace_addr == 0) | |||
224 | break; | |||
225 | trace->AddItem( | |||
226 | StructuredData::ObjectSP(new StructuredData::Integer(trace_addr))); | |||
227 | } | |||
228 | return trace; | |||
229 | } | |||
230 | ||||
231 | static StructuredData::Array *ConvertToStructuredArray( | |||
232 | ValueObjectSP return_value_sp, const std::string &items_name, | |||
233 | const std::string &count_name, | |||
234 | std::function<void(ValueObjectSP o, StructuredData::Dictionary *dict)> const | |||
235 | &callback) { | |||
236 | StructuredData::Array *array = new StructuredData::Array(); | |||
237 | unsigned int count = | |||
238 | return_value_sp->GetValueForExpressionPath(count_name.c_str()) | |||
239 | ->GetValueAsUnsigned(0); | |||
240 | ValueObjectSP objects = | |||
241 | return_value_sp->GetValueForExpressionPath(items_name.c_str()); | |||
242 | for (unsigned int i = 0; i < count; i++) { | |||
243 | ValueObjectSP o = objects->GetChildAtIndex(i, true); | |||
244 | StructuredData::Dictionary *dict = new StructuredData::Dictionary(); | |||
245 | ||||
246 | callback(o, dict); | |||
247 | ||||
248 | array->AddItem(StructuredData::ObjectSP(dict)); | |||
249 | } | |||
250 | return array; | |||
251 | } | |||
252 | ||||
253 | static std::string RetrieveString(ValueObjectSP return_value_sp, | |||
254 | ProcessSP process_sp, | |||
255 | const std::string &expression_path) { | |||
256 | addr_t ptr = | |||
257 | return_value_sp->GetValueForExpressionPath(expression_path.c_str()) | |||
258 | ->GetValueAsUnsigned(0); | |||
259 | std::string str; | |||
260 | Status error; | |||
261 | process_sp->ReadCStringFromMemory(ptr, str, error); | |||
262 | return str; | |||
263 | } | |||
264 | ||||
265 | static void | |||
266 | GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data, | |||
267 | std::map<uint64_t, user_id_t> &thread_id_map) { | |||
268 | ConvertToStructuredArray( | |||
269 | data, ".threads", ".thread_count", | |||
270 | [process_sp, &thread_id_map](ValueObjectSP o, | |||
271 | StructuredData::Dictionary *dict) { | |||
272 | uint64_t thread_id = | |||
273 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0); | |||
274 | uint64_t thread_os_id = | |||
275 | o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0); | |||
276 | user_id_t lldb_user_id = 0; | |||
277 | ||||
278 | bool can_update = true; | |||
279 | ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID( | |||
280 | thread_os_id, can_update); | |||
281 | if (lldb_thread) { | |||
282 | lldb_user_id = lldb_thread->GetIndexID(); | |||
283 | } else { | |||
284 | // This isn't a live thread anymore. Ask process to assign a new | |||
285 | // Index ID (or return an old one if we've already seen this | |||
286 | // thread_os_id). It will also make sure that no new threads are | |||
287 | // assigned this Index ID. | |||
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 InstrumentationRuntimeTSan::RetrieveReportData( | |||
305 | 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(process_sp->GetUtilityExpressionTimeout()); | |||
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) { | |||
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); | |||
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 | // "stacks" happen on the current thread | |||
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 | InstrumentationRuntimeTSan::FormatDescription(StructuredData::ObjectSP report) { | |||
492 | std::string description = std::string(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 | // for unknown report codes just show the code | |||
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 std::string(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(Mangled::ePreferMangled); | |||
570 | ||||
571 | ModuleSP module = symbol->CalculateSymbolContextModule(); | |||
572 | if (!module) | |||
573 | return; | |||
574 | ||||
575 | VariableList var_list; | |||
576 | module->FindGlobalVariables(sym_name, CompilerDeclContext(), 1U, var_list); | |||
577 | if (var_list.GetSize() < 1) | |||
578 | return; | |||
579 | ||||
580 | VariableSP var = var_list.GetVariableAtIndex(0); | |||
581 | decl = var->GetDeclaration(); | |||
582 | } | |||
583 | ||||
584 | addr_t InstrumentationRuntimeTSan::GetFirstNonInternalFramePc( | |||
585 | StructuredData::ObjectSP trace, bool skip_one_frame) { | |||
586 | ProcessSP process_sp = GetProcessSP(); | |||
587 | ModuleSP runtime_module_sp = GetRuntimeModuleSP(); | |||
588 | ||||
589 | StructuredData::Array *trace_array = trace->GetAsArray(); | |||
590 | for (size_t i = 0; i < trace_array->GetSize(); i++) { | |||
591 | if (skip_one_frame && i == 0) | |||
592 | continue; | |||
593 | ||||
594 | addr_t addr; | |||
595 | if (!trace_array->GetItemAtIndexAsInteger(i, addr)) | |||
596 | continue; | |||
597 | ||||
598 | lldb_private::Address so_addr; | |||
599 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress( | |||
600 | addr, so_addr)) | |||
601 | continue; | |||
602 | ||||
603 | if (so_addr.GetModule() == runtime_module_sp) | |||
604 | continue; | |||
605 | ||||
606 | return addr; | |||
607 | } | |||
608 | ||||
609 | return 0; | |||
610 | } | |||
611 | ||||
612 | std::string | |||
613 | InstrumentationRuntimeTSan::GenerateSummary(StructuredData::ObjectSP report) { | |||
614 | ProcessSP process_sp = GetProcessSP(); | |||
615 | ||||
616 | std::string summary = std::string(report->GetAsDictionary() | |||
617 | ->GetValueForKey("description") | |||
618 | ->GetAsString() | |||
619 | ->GetValue()); | |||
620 | bool skip_one_frame = | |||
621 | report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() == | |||
622 | "external-race"; | |||
623 | ||||
624 | addr_t pc = 0; | |||
625 | if (report->GetAsDictionary() | |||
626 | ->GetValueForKey("mops") | |||
627 | ->GetAsArray() | |||
628 | ->GetSize() > 0) | |||
629 | pc = GetFirstNonInternalFramePc(report->GetAsDictionary() | |||
630 | ->GetValueForKey("mops") | |||
631 | ->GetAsArray() | |||
632 | ->GetItemAtIndex(0) | |||
633 | ->GetAsDictionary() | |||
634 | ->GetValueForKey("trace"), | |||
635 | skip_one_frame); | |||
636 | ||||
637 | if (report->GetAsDictionary() | |||
638 | ->GetValueForKey("stacks") | |||
639 | ->GetAsArray() | |||
640 | ->GetSize() > 0) | |||
641 | pc = GetFirstNonInternalFramePc(report->GetAsDictionary() | |||
642 | ->GetValueForKey("stacks") | |||
643 | ->GetAsArray() | |||
644 | ->GetItemAtIndex(0) | |||
645 | ->GetAsDictionary() | |||
646 | ->GetValueForKey("trace"), | |||
647 | skip_one_frame); | |||
648 | ||||
649 | if (pc != 0) { | |||
650 | summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc); | |||
651 | } | |||
652 | ||||
653 | if (report->GetAsDictionary() | |||
654 | ->GetValueForKey("locs") | |||
655 | ->GetAsArray() | |||
656 | ->GetSize() > 0) { | |||
657 | StructuredData::ObjectSP loc = report->GetAsDictionary() | |||
658 | ->GetValueForKey("locs") | |||
659 | ->GetAsArray() | |||
660 | ->GetItemAtIndex(0); | |||
661 | std::string object_type = std::string(loc->GetAsDictionary() | |||
662 | ->GetValueForKey("object_type") | |||
663 | ->GetAsString() | |||
664 | ->GetValue()); | |||
665 | if (!object_type.empty()) { | |||
666 | summary = "Race on " + object_type + " object"; | |||
667 | } | |||
668 | addr_t addr = loc->GetAsDictionary() | |||
669 | ->GetValueForKey("address") | |||
670 | ->GetAsInteger() | |||
671 | ->GetValue(); | |||
672 | if (addr == 0) | |||
673 | addr = loc->GetAsDictionary() | |||
674 | ->GetValueForKey("start") | |||
675 | ->GetAsInteger() | |||
676 | ->GetValue(); | |||
677 | ||||
678 | if (addr != 0) { | |||
679 | std::string global_name = GetSymbolNameFromAddress(process_sp, addr); | |||
680 | if (!global_name.empty()) { | |||
681 | summary = summary + " at " + global_name; | |||
682 | } else { | |||
683 | summary = summary + " at " + Sprintf("0x%llx", addr); | |||
684 | } | |||
685 | } else { | |||
686 | int fd = loc->GetAsDictionary() | |||
687 | ->GetValueForKey("file_descriptor") | |||
688 | ->GetAsInteger() | |||
689 | ->GetValue(); | |||
690 | if (fd != 0) { | |||
691 | summary = summary + " on file descriptor " + Sprintf("%d", fd); | |||
692 | } | |||
693 | } | |||
694 | } | |||
695 | ||||
696 | return summary; | |||
697 | } | |||
698 | ||||
699 | addr_t InstrumentationRuntimeTSan::GetMainRacyAddress( | |||
700 | StructuredData::ObjectSP report) { | |||
701 | addr_t result = (addr_t)-1; | |||
702 | ||||
703 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( | |||
704 | [&result](StructuredData::Object *o) -> bool { | |||
705 | addr_t addr = | |||
706 | o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); | |||
707 | if (addr < result) | |||
708 | result = addr; | |||
709 | return true; | |||
710 | }); | |||
711 | ||||
712 | return (result == (addr_t)-1) ? 0 : result; | |||
713 | } | |||
714 | ||||
715 | std::string InstrumentationRuntimeTSan::GetLocationDescription( | |||
716 | StructuredData::ObjectSP report, addr_t &global_addr, | |||
717 | std::string &global_name, std::string &filename, uint32_t &line) { | |||
718 | std::string result = ""; | |||
719 | ||||
720 | ProcessSP process_sp = GetProcessSP(); | |||
721 | ||||
722 | if (report->GetAsDictionary() | |||
723 | ->GetValueForKey("locs") | |||
724 | ->GetAsArray() | |||
725 | ->GetSize() > 0) { | |||
726 | StructuredData::ObjectSP loc = report->GetAsDictionary() | |||
727 | ->GetValueForKey("locs") | |||
728 | ->GetAsArray() | |||
729 | ->GetItemAtIndex(0); | |||
730 | std::string type = std::string( | |||
731 | loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue()); | |||
732 | if (type == "global") { | |||
733 | global_addr = loc->GetAsDictionary() | |||
734 | ->GetValueForKey("address") | |||
735 | ->GetAsInteger() | |||
736 | ->GetValue(); | |||
737 | global_name = GetSymbolNameFromAddress(process_sp, global_addr); | |||
738 | if (!global_name.empty()) { | |||
739 | result = Sprintf("'%s' is a global variable (0x%llx)", | |||
740 | global_name.c_str(), global_addr); | |||
741 | } else { | |||
742 | result = Sprintf("0x%llx is a global variable", global_addr); | |||
743 | } | |||
744 | ||||
745 | Declaration decl; | |||
746 | GetSymbolDeclarationFromAddress(process_sp, global_addr, decl); | |||
747 | if (decl.GetFile()) { | |||
748 | filename = decl.GetFile().GetPath(); | |||
749 | line = decl.GetLine(); | |||
750 | } | |||
751 | } else if (type == "heap") { | |||
752 | addr_t addr = loc->GetAsDictionary() | |||
753 | ->GetValueForKey("start") | |||
754 | ->GetAsInteger() | |||
755 | ->GetValue(); | |||
756 | long size = loc->GetAsDictionary() | |||
757 | ->GetValueForKey("size") | |||
758 | ->GetAsInteger() | |||
759 | ->GetValue(); | |||
760 | std::string object_type = std::string(loc->GetAsDictionary() | |||
761 | ->GetValueForKey("object_type") | |||
762 | ->GetAsString() | |||
763 | ->GetValue()); | |||
764 | if (!object_type.empty()) { | |||
765 | result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size, | |||
766 | object_type.c_str(), addr); | |||
767 | } else { | |||
768 | result = | |||
769 | Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); | |||
770 | } | |||
771 | } else if (type == "stack") { | |||
772 | int tid = loc->GetAsDictionary() | |||
773 | ->GetValueForKey("thread_id") | |||
774 | ->GetAsInteger() | |||
775 | ->GetValue(); | |||
776 | result = Sprintf("Location is stack of thread %d", tid); | |||
777 | } else if (type == "tls") { | |||
778 | int tid = loc->GetAsDictionary() | |||
779 | ->GetValueForKey("thread_id") | |||
780 | ->GetAsInteger() | |||
781 | ->GetValue(); | |||
782 | result = Sprintf("Location is TLS of thread %d", tid); | |||
783 | } else if (type == "fd") { | |||
784 | int fd = loc->GetAsDictionary() | |||
785 | ->GetValueForKey("file_descriptor") | |||
786 | ->GetAsInteger() | |||
787 | ->GetValue(); | |||
788 | result = Sprintf("Location is file descriptor %d", fd); | |||
789 | } | |||
790 | } | |||
791 | ||||
792 | return result; | |||
793 | } | |||
794 | ||||
795 | bool InstrumentationRuntimeTSan::NotifyBreakpointHit( | |||
796 | void *baton, StoppointCallbackContext *context, user_id_t break_id, | |||
797 | user_id_t break_loc_id) { | |||
798 | assert(baton && "null baton")((void)0); | |||
799 | if (!baton) | |||
| ||||
800 | return false; | |||
801 | ||||
802 | InstrumentationRuntimeTSan *const instance = | |||
803 | static_cast<InstrumentationRuntimeTSan *>(baton); | |||
804 | ||||
805 | ProcessSP process_sp = instance->GetProcessSP(); | |||
806 | ||||
807 | if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) | |||
808 | return false; | |||
809 | ||||
810 | StructuredData::ObjectSP report = | |||
811 | instance->RetrieveReportData(context->exe_ctx_ref); | |||
812 | std::string stop_reason_description = | |||
813 | "unknown thread sanitizer fault (unable to extract thread sanitizer " | |||
814 | "report)"; | |||
815 | if (report) { | |||
816 | std::string issue_description = instance->FormatDescription(report); | |||
817 | report->GetAsDictionary()->AddStringItem("description", issue_description); | |||
818 | stop_reason_description = issue_description + " detected"; | |||
819 | report->GetAsDictionary()->AddStringItem("stop_description", | |||
820 | stop_reason_description); | |||
821 | std::string summary = instance->GenerateSummary(report); | |||
822 | report->GetAsDictionary()->AddStringItem("summary", summary); | |||
823 | addr_t main_address = instance->GetMainRacyAddress(report); | |||
824 | report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); | |||
825 | ||||
826 | addr_t global_addr = 0; | |||
827 | std::string global_name = ""; | |||
828 | std::string location_filename = ""; | |||
829 | uint32_t location_line = 0; | |||
830 | std::string location_description = instance->GetLocationDescription( | |||
831 | report, global_addr, global_name, location_filename, location_line); | |||
832 | report->GetAsDictionary()->AddStringItem("location_description", | |||
833 | location_description); | |||
834 | if (global_addr != 0) { | |||
835 | report->GetAsDictionary()->AddIntegerItem("global_address", global_addr); | |||
836 | } | |||
837 | if (!global_name.empty()) { | |||
838 | report->GetAsDictionary()->AddStringItem("global_name", global_name); | |||
839 | } | |||
840 | if (location_filename != "") { | |||
841 | report->GetAsDictionary()->AddStringItem("location_filename", | |||
842 | location_filename); | |||
843 | report->GetAsDictionary()->AddIntegerItem("location_line", location_line); | |||
844 | } | |||
845 | ||||
846 | bool all_addresses_are_same = true; | |||
847 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( | |||
848 | [&all_addresses_are_same, | |||
849 | main_address](StructuredData::Object *o) -> bool { | |||
850 | addr_t addr = | |||
851 | o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); | |||
852 | if (main_address != addr) | |||
853 | all_addresses_are_same = false; | |||
854 | return true; | |||
855 | }); | |||
856 | report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same", | |||
857 | all_addresses_are_same); | |||
858 | } | |||
859 | ||||
860 | // Make sure this is the right process | |||
861 | if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { | |||
862 | ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); | |||
863 | if (thread_sp) | |||
864 | thread_sp->SetStopInfo( | |||
865 | InstrumentationRuntimeStopInfo:: | |||
866 | CreateStopReasonWithInstrumentationData( | |||
867 | *thread_sp, stop_reason_description, report)); | |||
868 | ||||
869 | StreamFile &s = process_sp->GetTarget().GetDebugger().GetOutputStream(); | |||
870 | s.Printf("ThreadSanitizer report breakpoint hit. Use 'thread " | |||
871 | "info -s' to get extended information about the " | |||
872 | "report.\n"); | |||
873 | ||||
874 | return true; // Return true to stop the target | |||
875 | } else | |||
876 | return false; // Let target run | |||
877 | } | |||
878 | ||||
879 | const RegularExpression & | |||
880 | InstrumentationRuntimeTSan::GetPatternForRuntimeLibrary() { | |||
881 | static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_")); | |||
882 | return regex; | |||
883 | } | |||
884 | ||||
885 | bool InstrumentationRuntimeTSan::CheckIfRuntimeIsValid( | |||
886 | const lldb::ModuleSP module_sp) { | |||
887 | static ConstString g_tsan_get_current_report("__tsan_get_current_report"); | |||
888 | const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( | |||
889 | g_tsan_get_current_report, lldb::eSymbolTypeAny); | |||
890 | return symbol != nullptr; | |||
891 | } | |||
892 | ||||
893 | void InstrumentationRuntimeTSan::Activate() { | |||
894 | if (IsActive()) | |||
895 | return; | |||
896 | ||||
897 | ProcessSP process_sp = GetProcessSP(); | |||
898 | if (!process_sp) | |||
899 | return; | |||
900 | ||||
901 | ConstString symbol_name("__tsan_on_report"); | |||
902 | const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType( | |||
903 | symbol_name, eSymbolTypeCode); | |||
904 | ||||
905 | if (symbol == nullptr) | |||
906 | return; | |||
907 | ||||
908 | if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) | |||
909 | return; | |||
910 | ||||
911 | Target &target = process_sp->GetTarget(); | |||
912 | addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); | |||
913 | ||||
914 | if (symbol_address == LLDB_INVALID_ADDRESS0xffffffffffffffffULL) | |||
915 | return; | |||
916 | ||||
917 | bool internal = true; | |||
918 | bool hardware = false; | |||
919 | Breakpoint *breakpoint = | |||
920 | process_sp->GetTarget() | |||
921 | .CreateBreakpoint(symbol_address, internal, hardware) | |||
922 | .get(); | |||
923 | breakpoint->SetCallback(InstrumentationRuntimeTSan::NotifyBreakpointHit, this, | |||
924 | true); | |||
925 | breakpoint->SetBreakpointKind("thread-sanitizer-report"); | |||
926 | SetBreakpointID(breakpoint->GetID()); | |||
927 | ||||
928 | SetActive(true); | |||
929 | } | |||
930 | ||||
931 | void InstrumentationRuntimeTSan::Deactivate() { | |||
932 | if (GetBreakpointID() != LLDB_INVALID_BREAK_ID0) { | |||
933 | ProcessSP process_sp = GetProcessSP(); | |||
934 | if (process_sp) { | |||
935 | process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); | |||
936 | SetBreakpointID(LLDB_INVALID_BREAK_ID0); | |||
937 | } | |||
938 | } | |||
939 | SetActive(false); | |||
940 | } | |||
941 | static std::string GenerateThreadName(const std::string &path, | |||
942 | StructuredData::Object *o, | |||
943 | StructuredData::ObjectSP main_info) { | |||
944 | std::string result = "additional information"; | |||
945 | ||||
946 | if (path == "mops") { | |||
947 | int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue(); | |||
948 | int thread_id = | |||
949 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); | |||
950 | bool is_write = | |||
951 | o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); | |||
952 | bool is_atomic = | |||
953 | o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue(); | |||
954 | addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); | |||
955 | ||||
956 | std::string addr_string = Sprintf(" at 0x%llx", addr); | |||
957 | ||||
958 | if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same") | |||
959 | ->GetBooleanValue()) { | |||
960 | addr_string = ""; | |||
961 | } | |||
962 | ||||
963 | if (main_info->GetObjectForDotSeparatedPath("issue_type") | |||
964 | ->GetStringValue() == "external-race") { | |||
965 | result = Sprintf("%s access by thread %d", | |||
966 | is_write ? "mutating" : "read-only", thread_id); | |||
967 | } else if (main_info->GetObjectForDotSeparatedPath("issue_type") | |||
968 | ->GetStringValue() == "swift-access-race") { | |||
969 | result = Sprintf("modifying access by thread %d", thread_id); | |||
970 | } else { | |||
971 | result = Sprintf("%s%s of size %d%s by thread %d", | |||
972 | is_atomic ? "atomic " : "", is_write ? "write" : "read", | |||
973 | size, addr_string.c_str(), thread_id); | |||
974 | } | |||
975 | } | |||
976 | ||||
977 | if (path == "threads") { | |||
978 | int thread_id = | |||
979 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); | |||
980 | result = Sprintf("Thread %d created", thread_id); | |||
981 | } | |||
982 | ||||
983 | if (path == "locs") { | |||
984 | std::string type = std::string( | |||
985 | o->GetAsDictionary()->GetValueForKey("type")->GetStringValue()); | |||
986 | int thread_id = | |||
987 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); | |||
988 | int fd = | |||
989 | o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue(); | |||
990 | if (type == "heap") { | |||
991 | result = Sprintf("Heap block allocated by thread %d", thread_id); | |||
992 | } else if (type == "fd") { | |||
993 | result = | |||
994 | Sprintf("File descriptor %d created by thread %t", fd, thread_id); | |||
995 | } | |||
996 | } | |||
997 | ||||
998 | if (path == "mutexes") { | |||
999 | int mutex_id = | |||
1000 | o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue(); | |||
1001 | ||||
1002 | result = Sprintf("Mutex M%d created", mutex_id); | |||
1003 | } | |||
1004 | ||||
1005 | if (path == "stacks") { | |||
1006 | int thread_id = | |||
1007 | o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); | |||
1008 | result = Sprintf("Thread %d", thread_id); | |||
1009 | } | |||
1010 | ||||
1011 | result[0] = toupper(result[0]); | |||
1012 | ||||
1013 | return result; | |||
1014 | } | |||
1015 | ||||
1016 | static void AddThreadsForPath(const std::string &path, | |||
1017 | ThreadCollectionSP threads, ProcessSP process_sp, | |||
1018 | StructuredData::ObjectSP info) { | |||
1019 | info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach( | |||
1020 | [process_sp, threads, path, info](StructuredData::Object *o) -> bool { | |||
1021 | std::vector<lldb::addr_t> pcs; | |||
1022 | o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach( | |||
1023 | [&pcs](StructuredData::Object *pc) -> bool { | |||
1024 | pcs.push_back(pc->GetAsInteger()->GetValue()); | |||
1025 | return true; | |||
1026 | }); | |||
1027 | ||||
1028 | if (pcs.size() == 0) | |||
1029 | return true; | |||
1030 | ||||
1031 | StructuredData::ObjectSP thread_id_obj = | |||
1032 | o->GetObjectForDotSeparatedPath("thread_os_id"); | |||
1033 | tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; | |||
1034 | ||||
1035 | HistoryThread *history_thread = | |||
1036 | new HistoryThread(*process_sp, tid, pcs); | |||
1037 | ThreadSP new_thread_sp(history_thread); | |||
1038 | new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str()); | |||
1039 | ||||
1040 | // Save this in the Process' ExtendedThreadList so a strong pointer | |||
1041 | // retains the object | |||
1042 | process_sp->GetExtendedThreadList().AddThread(new_thread_sp); | |||
1043 | threads->AddThread(new_thread_sp); | |||
1044 | ||||
1045 | return true; | |||
1046 | }); | |||
1047 | } | |||
1048 | ||||
1049 | lldb::ThreadCollectionSP | |||
1050 | InstrumentationRuntimeTSan::GetBacktracesFromExtendedStopInfo( | |||
1051 | StructuredData::ObjectSP info) { | |||
1052 | ThreadCollectionSP threads; | |||
1053 | threads = std::make_shared<ThreadCollection>(); | |||
1054 | ||||
1055 | if (info->GetObjectForDotSeparatedPath("instrumentation_class") | |||
1056 | ->GetStringValue() != "ThreadSanitizer") | |||
1057 | return threads; | |||
1058 | ||||
1059 | ProcessSP process_sp = GetProcessSP(); | |||
1060 | ||||
1061 | AddThreadsForPath("stacks", threads, process_sp, info); | |||
1062 | AddThreadsForPath("mops", threads, process_sp, info); | |||
1063 | AddThreadsForPath("locs", threads, process_sp, info); | |||
1064 | AddThreadsForPath("mutexes", threads, process_sp, info); | |||
1065 | AddThreadsForPath("threads", threads, process_sp, info); | |||
1066 | ||||
1067 | return threads; | |||
1068 | } |