File: | tools/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp |
Warning: | line 293, column 1 Potential memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- TSanRuntime.cpp -----------------------------------------*- C++ -*-===// | |||
2 | // | |||
3 | // The LLVM Compiler Infrastructure | |||
4 | // | |||
5 | // This file is distributed under the University of Illinois Open Source | |||
6 | // License. See LICENSE.TXT for details. | |||
7 | // | |||
8 | //===----------------------------------------------------------------------===// | |||
9 | ||||
10 | #include "TSanRuntime.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++) { | |||
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( | |||
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 | // This isn't a live thread anymore. Ask process to assign a new | |||
284 | // Index ID (or return an old one if we've already seen this | |||
285 | // thread_os_id). | |||
286 | // It will also make sure that no new threads are assigned this Index | |||
287 | // 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 | |||
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) { | |||
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 | 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 | // 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 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")(static_cast <bool> (baton && "null baton") ? void (0) : __assert_fail ("baton && \"null baton\"", "/build/llvm-toolchain-snapshot-7~svn326425/tools/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp" , 799, __extension__ __PRETTY_FUNCTION__)); | |||
800 | if (!baton) | |||
| ||||
801 | return false; | |||
802 | ||||
803 | ThreadSanitizerRuntime *const instance = | |||
804 | static_cast<ThreadSanitizerRuntime *>(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(context->exe_ctx_ref); | |||
813 | std::string stop_reason_description; | |||
814 | if (report) { | |||
815 | std::string issue_description = instance->FormatDescription(report); | |||
816 | report->GetAsDictionary()->AddStringItem("description", issue_description); | |||
817 | stop_reason_description = issue_description + " detected"; | |||
818 | report->GetAsDictionary()->AddStringItem("stop_description", | |||
819 | stop_reason_description); | |||
820 | std::string summary = instance->GenerateSummary(report); | |||
821 | report->GetAsDictionary()->AddStringItem("summary", summary); | |||
822 | addr_t main_address = instance->GetMainRacyAddress(report); | |||
823 | report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); | |||
824 | ||||
825 | addr_t global_addr = 0; | |||
826 | std::string global_name = ""; | |||
827 | std::string location_filename = ""; | |||
828 | uint32_t location_line = 0; | |||
829 | std::string location_description = instance->GetLocationDescription( | |||
830 | report, global_addr, global_name, location_filename, location_line); | |||
831 | report->GetAsDictionary()->AddStringItem("location_description", | |||
832 | location_description); | |||
833 | if (global_addr != 0) { | |||
834 | report->GetAsDictionary()->AddIntegerItem("global_address", global_addr); | |||
835 | } | |||
836 | if (!global_name.empty()) { | |||
837 | report->GetAsDictionary()->AddStringItem("global_name", global_name); | |||
838 | } | |||
839 | if (location_filename != "") { | |||
840 | report->GetAsDictionary()->AddStringItem("location_filename", | |||
841 | location_filename); | |||
842 | report->GetAsDictionary()->AddIntegerItem("location_line", location_line); | |||
843 | } | |||
844 | ||||
845 | bool all_addresses_are_same = true; | |||
846 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( | |||
847 | [&all_addresses_are_same, | |||
848 | main_address](StructuredData::Object *o) -> bool { | |||
849 | addr_t addr = | |||
850 | o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); | |||
851 | if (main_address != addr) | |||
852 | all_addresses_are_same = false; | |||
853 | return true; | |||
854 | }); | |||
855 | report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same", | |||
856 | all_addresses_are_same); | |||
857 | } | |||
858 | ||||
859 | // Make sure this is the right process | |||
860 | if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { | |||
861 | ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); | |||
862 | if (thread_sp) | |||
863 | thread_sp->SetStopInfo( | |||
864 | InstrumentationRuntimeStopInfo:: | |||
865 | CreateStopReasonWithInstrumentationData( | |||
866 | *thread_sp, stop_reason_description, report)); | |||
867 | ||||
868 | StreamFileSP stream_sp( | |||
869 | process_sp->GetTarget().GetDebugger().GetOutputFile()); | |||
870 | if (stream_sp) { | |||
871 | stream_sp->Printf("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 &ThreadSanitizerRuntime::GetPatternForRuntimeLibrary() { | |||
881 | static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_")); | |||
882 | return regex; | |||
883 | } | |||
884 | ||||
885 | bool ThreadSanitizerRuntime::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 ThreadSanitizerRuntime::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 == NULL__null) | |||
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_ADDRESS(18446744073709551615UL)) | |||
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(ThreadSanitizerRuntime::NotifyBreakpointHit, this, | |||
924 | true); | |||
925 | breakpoint->SetBreakpointKind("thread-sanitizer-report"); | |||
926 | SetBreakpointID(breakpoint->GetID()); | |||
927 | ||||
928 | SetActive(true); | |||
929 | } | |||
930 | ||||
931 | void ThreadSanitizerRuntime::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 = | |||
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 | uint32_t stop_id = 0; | |||
1036 | bool stop_id_is_valid = false; | |||
1037 | HistoryThread *history_thread = | |||
1038 | new HistoryThread(*process_sp, tid, pcs, stop_id, stop_id_is_valid); | |||
1039 | ThreadSP new_thread_sp(history_thread); | |||
1040 | new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str()); | |||
1041 | ||||
1042 | // Save this in the Process' ExtendedThreadList so a strong pointer | |||
1043 | // retains the object | |||
1044 | process_sp->GetExtendedThreadList().AddThread(new_thread_sp); | |||
1045 | threads->AddThread(new_thread_sp); | |||
1046 | ||||
1047 | return true; | |||
1048 | }); | |||
1049 | } | |||
1050 | ||||
1051 | lldb::ThreadCollectionSP | |||
1052 | ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo( | |||
1053 | StructuredData::ObjectSP info) { | |||
1054 | ThreadCollectionSP threads; | |||
1055 | threads.reset(new ThreadCollection()); | |||
1056 | ||||
1057 | if (info->GetObjectForDotSeparatedPath("instrumentation_class") | |||
1058 | ->GetStringValue() != "ThreadSanitizer") | |||
1059 | return threads; | |||
1060 | ||||
1061 | ProcessSP process_sp = GetProcessSP(); | |||
1062 | ||||
1063 | AddThreadsForPath("stacks", threads, process_sp, info); | |||
1064 | AddThreadsForPath("mops", threads, process_sp, info); | |||
1065 | AddThreadsForPath("locs", threads, process_sp, info); | |||
1066 | AddThreadsForPath("mutexes", threads, process_sp, info); | |||
1067 | AddThreadsForPath("threads", threads, process_sp, info); | |||
1068 | ||||
1069 | return threads; | |||
1070 | } |