Path: blob/main/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp
39654 views
//===-- InstrumentationRuntimeTSan.cpp ------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "InstrumentationRuntimeTSan.h"910#include "Plugins/Process/Utility/HistoryThread.h"11#include "lldb/Breakpoint/StoppointCallbackContext.h"12#include "lldb/Core/Debugger.h"13#include "lldb/Core/Module.h"14#include "lldb/Core/PluginInterface.h"15#include "lldb/Core/PluginManager.h"16#include "lldb/Core/ValueObject.h"17#include "lldb/Expression/UserExpression.h"18#include "lldb/Host/StreamFile.h"19#include "lldb/Interpreter/CommandReturnObject.h"20#include "lldb/Symbol/Symbol.h"21#include "lldb/Symbol/SymbolContext.h"22#include "lldb/Symbol/Variable.h"23#include "lldb/Symbol/VariableList.h"24#include "lldb/Target/InstrumentationRuntimeStopInfo.h"25#include "lldb/Target/SectionLoadList.h"26#include "lldb/Target/StopInfo.h"27#include "lldb/Target/Target.h"28#include "lldb/Target/Thread.h"29#include "lldb/Utility/LLDBLog.h"30#include "lldb/Utility/Log.h"31#include "lldb/Utility/RegularExpression.h"32#include "lldb/Utility/Stream.h"3334#include <memory>3536using namespace lldb;37using namespace lldb_private;3839LLDB_PLUGIN_DEFINE(InstrumentationRuntimeTSan)4041lldb::InstrumentationRuntimeSP42InstrumentationRuntimeTSan::CreateInstance(const lldb::ProcessSP &process_sp) {43return InstrumentationRuntimeSP(new InstrumentationRuntimeTSan(process_sp));44}4546void InstrumentationRuntimeTSan::Initialize() {47PluginManager::RegisterPlugin(48GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.",49CreateInstance, GetTypeStatic);50}5152void InstrumentationRuntimeTSan::Terminate() {53PluginManager::UnregisterPlugin(CreateInstance);54}5556lldb::InstrumentationRuntimeType InstrumentationRuntimeTSan::GetTypeStatic() {57return eInstrumentationRuntimeTypeThreadSanitizer;58}5960InstrumentationRuntimeTSan::~InstrumentationRuntimeTSan() { Deactivate(); }6162const char *thread_sanitizer_retrieve_report_data_prefix = R"(63extern "C"64{65void *__tsan_get_current_report();66int __tsan_get_report_data(void *report, const char **description, int *count,67int *stack_count, int *mop_count, int *loc_count,68int *mutex_count, int *thread_count,69int *unique_tid_count, void **sleep_trace,70unsigned long trace_size);71int __tsan_get_report_stack(void *report, unsigned long idx, void **trace,72unsigned long trace_size);73int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr,74int *size, int *write, int *atomic, void **trace,75unsigned long trace_size);76int __tsan_get_report_loc(void *report, unsigned long idx, const char **type,77void **addr, unsigned long *start, unsigned long *size, int *tid,78int *fd, int *suppressable, void **trace,79unsigned long trace_size);80int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr,81int *destroyed, void **trace, unsigned long trace_size);82int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id,83int *running, const char **name, int *parent_tid,84void **trace, unsigned long trace_size);85int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid);8687// TODO: dlsym won't work on Windows.88void *dlsym(void* handle, const char* symbol);89int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type);90}91)";9293const char *thread_sanitizer_retrieve_report_data_command = R"(9495const int REPORT_TRACE_SIZE = 128;96const int REPORT_ARRAY_SIZE = 4;9798struct {99void *report;100const char *description;101int report_count;102103void *sleep_trace[REPORT_TRACE_SIZE];104105int stack_count;106struct {107int idx;108void *trace[REPORT_TRACE_SIZE];109} stacks[REPORT_ARRAY_SIZE];110111int mop_count;112struct {113int idx;114int tid;115int size;116int write;117int atomic;118void *addr;119void *trace[REPORT_TRACE_SIZE];120} mops[REPORT_ARRAY_SIZE];121122int loc_count;123struct {124int idx;125const char *type;126void *addr;127unsigned long start;128unsigned long size;129int tid;130int fd;131int suppressable;132void *trace[REPORT_TRACE_SIZE];133const char *object_type;134} locs[REPORT_ARRAY_SIZE];135136int mutex_count;137struct {138int idx;139unsigned long mutex_id;140void *addr;141int destroyed;142void *trace[REPORT_TRACE_SIZE];143} mutexes[REPORT_ARRAY_SIZE];144145int thread_count;146struct {147int idx;148int tid;149unsigned long os_id;150int running;151const char *name;152int parent_tid;153void *trace[REPORT_TRACE_SIZE];154} threads[REPORT_ARRAY_SIZE];155156int unique_tid_count;157struct {158int idx;159int tid;160} unique_tids[REPORT_ARRAY_SIZE];161} t = {0};162163ptr__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");164165t.report = __tsan_get_current_report();166__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);167168if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE;169for (int i = 0; i < t.stack_count; i++) {170t.stacks[i].idx = i;171__tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE);172}173174if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE;175for (int i = 0; i < t.mop_count; i++) {176t.mops[i].idx = i;177__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);178}179180if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE;181for (int i = 0; i < t.loc_count; i++) {182t.locs[i].idx = i;183__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);184if (ptr__tsan_get_report_loc_object_type)185ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type);186}187188if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE;189for (int i = 0; i < t.mutex_count; i++) {190t.mutexes[i].idx = i;191__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);192}193194if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE;195for (int i = 0; i < t.thread_count; i++) {196t.threads[i].idx = i;197__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);198}199200if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE;201for (int i = 0; i < t.unique_tid_count; i++) {202t.unique_tids[i].idx = i;203__tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid);204}205206t;207)";208209static StructuredData::ArraySP210CreateStackTrace(ValueObjectSP o,211const std::string &trace_item_name = ".trace") {212auto trace_sp = std::make_shared<StructuredData::Array>();213ValueObjectSP trace_value_object =214o->GetValueForExpressionPath(trace_item_name.c_str());215size_t count = trace_value_object->GetNumChildrenIgnoringErrors();216for (size_t j = 0; j < count; j++) {217addr_t trace_addr =218trace_value_object->GetChildAtIndex(j)->GetValueAsUnsigned(0);219if (trace_addr == 0)220break;221trace_sp->AddIntegerItem(trace_addr);222}223return trace_sp;224}225226static StructuredData::ArraySP ConvertToStructuredArray(227ValueObjectSP return_value_sp, const std::string &items_name,228const std::string &count_name,229std::function<void(const ValueObjectSP &o,230const StructuredData::DictionarySP &dict)> const231&callback) {232auto array_sp = std::make_shared<StructuredData::Array>();233unsigned int count =234return_value_sp->GetValueForExpressionPath(count_name.c_str())235->GetValueAsUnsigned(0);236ValueObjectSP objects =237return_value_sp->GetValueForExpressionPath(items_name.c_str());238for (unsigned int i = 0; i < count; i++) {239ValueObjectSP o = objects->GetChildAtIndex(i);240auto dict_sp = std::make_shared<StructuredData::Dictionary>();241242callback(o, dict_sp);243244array_sp->AddItem(dict_sp);245}246return array_sp;247}248249static std::string RetrieveString(ValueObjectSP return_value_sp,250ProcessSP process_sp,251const std::string &expression_path) {252addr_t ptr =253return_value_sp->GetValueForExpressionPath(expression_path.c_str())254->GetValueAsUnsigned(0);255std::string str;256Status error;257process_sp->ReadCStringFromMemory(ptr, str, error);258return str;259}260261static void262GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data,263std::map<uint64_t, user_id_t> &thread_id_map) {264ConvertToStructuredArray(265data, ".threads", ".thread_count",266[process_sp, &thread_id_map](const ValueObjectSP &o,267const StructuredData::DictionarySP &dict) {268uint64_t thread_id =269o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0);270uint64_t thread_os_id =271o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0);272user_id_t lldb_user_id = 0;273274bool can_update = true;275ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID(276thread_os_id, can_update);277if (lldb_thread) {278lldb_user_id = lldb_thread->GetIndexID();279} else {280// This isn't a live thread anymore. Ask process to assign a new281// Index ID (or return an old one if we've already seen this282// thread_os_id). It will also make sure that no new threads are283// assigned this Index ID.284lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id);285}286287thread_id_map[thread_id] = lldb_user_id;288});289}290291static user_id_t Renumber(uint64_t id,292std::map<uint64_t, user_id_t> &thread_id_map) {293auto IT = thread_id_map.find(id);294if (IT == thread_id_map.end())295return 0;296297return IT->second;298}299300StructuredData::ObjectSP InstrumentationRuntimeTSan::RetrieveReportData(301ExecutionContextRef exe_ctx_ref) {302ProcessSP process_sp = GetProcessSP();303if (!process_sp)304return StructuredData::ObjectSP();305306ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();307StackFrameSP frame_sp =308thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);309310if (!frame_sp)311return StructuredData::ObjectSP();312313EvaluateExpressionOptions options;314options.SetUnwindOnError(true);315options.SetTryAllThreads(true);316options.SetStopOthers(true);317options.SetIgnoreBreakpoints(true);318options.SetTimeout(process_sp->GetUtilityExpressionTimeout());319options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix);320options.SetAutoApplyFixIts(false);321options.SetLanguage(eLanguageTypeObjC_plus_plus);322323ValueObjectSP main_value;324ExecutionContext exe_ctx;325Status eval_error;326frame_sp->CalculateExecutionContext(exe_ctx);327ExpressionResults result = UserExpression::Evaluate(328exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "",329main_value, eval_error);330if (result != eExpressionCompleted) {331StreamString ss;332ss << "cannot evaluate ThreadSanitizer expression:\n";333ss << eval_error.AsCString();334Debugger::ReportWarning(ss.GetString().str(),335process_sp->GetTarget().GetDebugger().GetID());336return StructuredData::ObjectSP();337}338339std::map<uint64_t, user_id_t> thread_id_map;340GetRenumberedThreadIds(process_sp, main_value, thread_id_map);341342auto dict = std::make_shared<StructuredData::Dictionary>();343dict->AddStringItem("instrumentation_class", "ThreadSanitizer");344dict->AddStringItem("issue_type",345RetrieveString(main_value, process_sp, ".description"));346dict->AddIntegerItem("report_count",347main_value->GetValueForExpressionPath(".report_count")348->GetValueAsUnsigned(0));349dict->AddItem("sleep_trace", CreateStackTrace(350main_value, ".sleep_trace"));351352StructuredData::ArraySP stacks = ConvertToStructuredArray(353main_value, ".stacks", ".stack_count",354[thread_sp](const ValueObjectSP &o,355const StructuredData::DictionarySP &dict) {356dict->AddIntegerItem(357"index",358o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));359dict->AddItem("trace", CreateStackTrace(o));360// "stacks" happen on the current thread361dict->AddIntegerItem("thread_id", thread_sp->GetIndexID());362});363dict->AddItem("stacks", stacks);364365StructuredData::ArraySP mops = ConvertToStructuredArray(366main_value, ".mops", ".mop_count",367[&thread_id_map](const ValueObjectSP &o,368const StructuredData::DictionarySP &dict) {369dict->AddIntegerItem(370"index",371o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));372dict->AddIntegerItem(373"thread_id",374Renumber(375o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),376thread_id_map));377dict->AddIntegerItem(378"size",379o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0));380dict->AddBooleanItem(381"is_write",382o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0));383dict->AddBooleanItem(384"is_atomic",385o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0));386dict->AddIntegerItem(387"address",388o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));389dict->AddItem("trace", CreateStackTrace(o));390});391dict->AddItem("mops", mops);392393StructuredData::ArraySP locs = ConvertToStructuredArray(394main_value, ".locs", ".loc_count",395[process_sp, &thread_id_map](const ValueObjectSP &o,396const StructuredData::DictionarySP &dict) {397dict->AddIntegerItem(398"index",399o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));400dict->AddStringItem("type", RetrieveString(o, process_sp, ".type"));401dict->AddIntegerItem(402"address",403o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));404dict->AddIntegerItem(405"start",406o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0));407dict->AddIntegerItem(408"size",409o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0));410dict->AddIntegerItem(411"thread_id",412Renumber(413o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),414thread_id_map));415dict->AddIntegerItem(416"file_descriptor",417o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0));418dict->AddIntegerItem("suppressable",419o->GetValueForExpressionPath(".suppressable")420->GetValueAsUnsigned(0));421dict->AddItem("trace", CreateStackTrace(o));422dict->AddStringItem("object_type",423RetrieveString(o, process_sp, ".object_type"));424});425dict->AddItem("locs", locs);426427StructuredData::ArraySP mutexes = ConvertToStructuredArray(428main_value, ".mutexes", ".mutex_count",429[](const ValueObjectSP &o, const StructuredData::DictionarySP &dict) {430dict->AddIntegerItem(431"index",432o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));433dict->AddIntegerItem(434"mutex_id",435o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0));436dict->AddIntegerItem(437"address",438o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));439dict->AddIntegerItem(440"destroyed",441o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0));442dict->AddItem("trace", CreateStackTrace(o));443});444dict->AddItem("mutexes", mutexes);445446StructuredData::ArraySP threads = ConvertToStructuredArray(447main_value, ".threads", ".thread_count",448[process_sp, &thread_id_map](const ValueObjectSP &o,449const StructuredData::DictionarySP &dict) {450dict->AddIntegerItem(451"index",452o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));453dict->AddIntegerItem(454"thread_id",455Renumber(456o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),457thread_id_map));458dict->AddIntegerItem(459"thread_os_id",460o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0));461dict->AddIntegerItem(462"running",463o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0));464dict->AddStringItem("name", RetrieveString(o, process_sp, ".name"));465dict->AddIntegerItem(466"parent_thread_id",467Renumber(o->GetValueForExpressionPath(".parent_tid")468->GetValueAsUnsigned(0),469thread_id_map));470dict->AddItem("trace", CreateStackTrace(o));471});472dict->AddItem("threads", threads);473474StructuredData::ArraySP unique_tids = ConvertToStructuredArray(475main_value, ".unique_tids", ".unique_tid_count",476[&thread_id_map](const ValueObjectSP &o,477const StructuredData::DictionarySP &dict) {478dict->AddIntegerItem(479"index",480o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));481dict->AddIntegerItem(482"tid",483Renumber(484o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),485thread_id_map));486});487dict->AddItem("unique_tids", unique_tids);488489return dict;490}491492std::string493InstrumentationRuntimeTSan::FormatDescription(StructuredData::ObjectSP report) {494std::string description = std::string(report->GetAsDictionary()495->GetValueForKey("issue_type")496->GetAsString()497->GetValue());498499if (description == "data-race") {500return "Data race";501} else if (description == "data-race-vptr") {502return "Data race on C++ virtual pointer";503} else if (description == "heap-use-after-free") {504return "Use of deallocated memory";505} else if (description == "heap-use-after-free-vptr") {506return "Use of deallocated C++ virtual pointer";507} else if (description == "thread-leak") {508return "Thread leak";509} else if (description == "locked-mutex-destroy") {510return "Destruction of a locked mutex";511} else if (description == "mutex-double-lock") {512return "Double lock of a mutex";513} else if (description == "mutex-invalid-access") {514return "Use of an uninitialized or destroyed mutex";515} else if (description == "mutex-bad-unlock") {516return "Unlock of an unlocked mutex (or by a wrong thread)";517} else if (description == "mutex-bad-read-lock") {518return "Read lock of a write locked mutex";519} else if (description == "mutex-bad-read-unlock") {520return "Read unlock of a write locked mutex";521} else if (description == "signal-unsafe-call") {522return "Signal-unsafe call inside a signal handler";523} else if (description == "errno-in-signal-handler") {524return "Overwrite of errno in a signal handler";525} else if (description == "lock-order-inversion") {526return "Lock order inversion (potential deadlock)";527} else if (description == "external-race") {528return "Race on a library object";529} else if (description == "swift-access-race") {530return "Swift access race";531}532533// for unknown report codes just show the code534return description;535}536537static std::string Sprintf(const char *format, ...) {538StreamString s;539va_list args;540va_start(args, format);541s.PrintfVarArg(format, args);542va_end(args);543return std::string(s.GetString());544}545546static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) {547lldb_private::Address so_addr;548if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr,549so_addr))550return "";551552lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol();553if (!symbol)554return "";555556std::string sym_name = symbol->GetName().GetCString();557return sym_name;558}559560static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr,561Declaration &decl) {562lldb_private::Address so_addr;563if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr,564so_addr))565return;566567lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol();568if (!symbol)569return;570571ConstString sym_name = symbol->GetMangled().GetName(Mangled::ePreferMangled);572573ModuleSP module = symbol->CalculateSymbolContextModule();574if (!module)575return;576577VariableList var_list;578module->FindGlobalVariables(sym_name, CompilerDeclContext(), 1U, var_list);579if (var_list.GetSize() < 1)580return;581582VariableSP var = var_list.GetVariableAtIndex(0);583decl = var->GetDeclaration();584}585586addr_t InstrumentationRuntimeTSan::GetFirstNonInternalFramePc(587StructuredData::ObjectSP trace, bool skip_one_frame) {588ProcessSP process_sp = GetProcessSP();589ModuleSP runtime_module_sp = GetRuntimeModuleSP();590591StructuredData::Array *trace_array = trace->GetAsArray();592for (size_t i = 0; i < trace_array->GetSize(); i++) {593if (skip_one_frame && i == 0)594continue;595596auto maybe_addr = trace_array->GetItemAtIndexAsInteger<addr_t>(i);597if (!maybe_addr)598continue;599addr_t addr = *maybe_addr;600601lldb_private::Address so_addr;602if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(603addr, so_addr))604continue;605606if (so_addr.GetModule() == runtime_module_sp)607continue;608609return addr;610}611612return 0;613}614615std::string616InstrumentationRuntimeTSan::GenerateSummary(StructuredData::ObjectSP report) {617ProcessSP process_sp = GetProcessSP();618619std::string summary = std::string(report->GetAsDictionary()620->GetValueForKey("description")621->GetAsString()622->GetValue());623bool skip_one_frame =624report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() ==625"external-race";626627addr_t pc = 0;628if (report->GetAsDictionary()629->GetValueForKey("mops")630->GetAsArray()631->GetSize() > 0)632pc = GetFirstNonInternalFramePc(report->GetAsDictionary()633->GetValueForKey("mops")634->GetAsArray()635->GetItemAtIndex(0)636->GetAsDictionary()637->GetValueForKey("trace"),638skip_one_frame);639640if (report->GetAsDictionary()641->GetValueForKey("stacks")642->GetAsArray()643->GetSize() > 0)644pc = GetFirstNonInternalFramePc(report->GetAsDictionary()645->GetValueForKey("stacks")646->GetAsArray()647->GetItemAtIndex(0)648->GetAsDictionary()649->GetValueForKey("trace"),650skip_one_frame);651652if (pc != 0) {653summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc);654}655656if (report->GetAsDictionary()657->GetValueForKey("locs")658->GetAsArray()659->GetSize() > 0) {660StructuredData::ObjectSP loc = report->GetAsDictionary()661->GetValueForKey("locs")662->GetAsArray()663->GetItemAtIndex(0);664std::string object_type = std::string(loc->GetAsDictionary()665->GetValueForKey("object_type")666->GetAsString()667->GetValue());668if (!object_type.empty()) {669summary = "Race on " + object_type + " object";670}671addr_t addr = loc->GetAsDictionary()672->GetValueForKey("address")673->GetUnsignedIntegerValue();674if (addr == 0)675addr = loc->GetAsDictionary()676->GetValueForKey("start")677->GetUnsignedIntegerValue();678679if (addr != 0) {680std::string global_name = GetSymbolNameFromAddress(process_sp, addr);681if (!global_name.empty()) {682summary = summary + " at " + global_name;683} else {684summary = summary + " at " + Sprintf("0x%llx", addr);685}686} else {687int fd = loc->GetAsDictionary()688->GetValueForKey("file_descriptor")689->GetSignedIntegerValue();690if (fd != 0) {691summary = summary + " on file descriptor " + Sprintf("%d", fd);692}693}694}695696return summary;697}698699addr_t InstrumentationRuntimeTSan::GetMainRacyAddress(700StructuredData::ObjectSP report) {701addr_t result = (addr_t)-1;702703report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach(704[&result](StructuredData::Object *o) -> bool {705addr_t addr = o->GetObjectForDotSeparatedPath("address")706->GetUnsignedIntegerValue();707if (addr < result)708result = addr;709return true;710});711712return (result == (addr_t)-1) ? 0 : result;713}714715std::string InstrumentationRuntimeTSan::GetLocationDescription(716StructuredData::ObjectSP report, addr_t &global_addr,717std::string &global_name, std::string &filename, uint32_t &line) {718std::string result;719720ProcessSP process_sp = GetProcessSP();721722if (report->GetAsDictionary()723->GetValueForKey("locs")724->GetAsArray()725->GetSize() > 0) {726StructuredData::ObjectSP loc = report->GetAsDictionary()727->GetValueForKey("locs")728->GetAsArray()729->GetItemAtIndex(0);730std::string type = std::string(731loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue());732if (type == "global") {733global_addr = loc->GetAsDictionary()734->GetValueForKey("address")735->GetUnsignedIntegerValue();736737global_name = GetSymbolNameFromAddress(process_sp, global_addr);738if (!global_name.empty()) {739result = Sprintf("'%s' is a global variable (0x%llx)",740global_name.c_str(), global_addr);741} else {742result = Sprintf("0x%llx is a global variable", global_addr);743}744745Declaration decl;746GetSymbolDeclarationFromAddress(process_sp, global_addr, decl);747if (decl.GetFile()) {748filename = decl.GetFile().GetPath();749line = decl.GetLine();750}751} else if (type == "heap") {752addr_t addr = loc->GetAsDictionary()753->GetValueForKey("start")754->GetUnsignedIntegerValue();755756size_t size = loc->GetAsDictionary()757->GetValueForKey("size")758->GetUnsignedIntegerValue();759760std::string object_type = std::string(loc->GetAsDictionary()761->GetValueForKey("object_type")762->GetAsString()763->GetValue());764if (!object_type.empty()) {765result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size,766object_type.c_str(), addr);767} else {768result =769Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr);770}771} else if (type == "stack") {772tid_t tid = loc->GetAsDictionary()773->GetValueForKey("thread_id")774->GetUnsignedIntegerValue();775776result = Sprintf("Location is stack of thread %d", tid);777} else if (type == "tls") {778tid_t tid = loc->GetAsDictionary()779->GetValueForKey("thread_id")780->GetUnsignedIntegerValue();781782result = Sprintf("Location is TLS of thread %d", tid);783} else if (type == "fd") {784int fd = loc->GetAsDictionary()785->GetValueForKey("file_descriptor")786->GetSignedIntegerValue();787788result = Sprintf("Location is file descriptor %d", fd);789}790}791792return result;793}794795bool InstrumentationRuntimeTSan::NotifyBreakpointHit(796void *baton, StoppointCallbackContext *context, user_id_t break_id,797user_id_t break_loc_id) {798assert(baton && "null baton");799if (!baton)800return false;801802InstrumentationRuntimeTSan *const instance =803static_cast<InstrumentationRuntimeTSan *>(baton);804805ProcessSP process_sp = instance->GetProcessSP();806807if (process_sp->GetModIDRef().IsLastResumeForUserExpression())808return false;809810StructuredData::ObjectSP report =811instance->RetrieveReportData(context->exe_ctx_ref);812std::string stop_reason_description =813"unknown thread sanitizer fault (unable to extract thread sanitizer "814"report)";815if (report) {816std::string issue_description = instance->FormatDescription(report);817report->GetAsDictionary()->AddStringItem("description", issue_description);818stop_reason_description = issue_description + " detected";819report->GetAsDictionary()->AddStringItem("stop_description",820stop_reason_description);821std::string summary = instance->GenerateSummary(report);822report->GetAsDictionary()->AddStringItem("summary", summary);823addr_t main_address = instance->GetMainRacyAddress(report);824report->GetAsDictionary()->AddIntegerItem("memory_address", main_address);825826addr_t global_addr = 0;827std::string global_name;828std::string location_filename;829uint32_t location_line = 0;830std::string location_description = instance->GetLocationDescription(831report, global_addr, global_name, location_filename, location_line);832report->GetAsDictionary()->AddStringItem("location_description",833location_description);834if (global_addr != 0) {835report->GetAsDictionary()->AddIntegerItem("global_address", global_addr);836}837if (!global_name.empty()) {838report->GetAsDictionary()->AddStringItem("global_name", global_name);839}840if (location_filename != "") {841report->GetAsDictionary()->AddStringItem("location_filename",842location_filename);843report->GetAsDictionary()->AddIntegerItem("location_line", location_line);844}845846bool all_addresses_are_same = true;847report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach(848[&all_addresses_are_same,849main_address](StructuredData::Object *o) -> bool {850addr_t addr = o->GetObjectForDotSeparatedPath("address")851->GetUnsignedIntegerValue();852if (main_address != addr)853all_addresses_are_same = false;854return true;855});856report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same",857all_addresses_are_same);858}859860// Make sure this is the right process861if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {862ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();863if (thread_sp)864thread_sp->SetStopInfo(865InstrumentationRuntimeStopInfo::866CreateStopReasonWithInstrumentationData(867*thread_sp, stop_reason_description, report));868869StreamFile &s = process_sp->GetTarget().GetDebugger().GetOutputStream();870s.Printf("ThreadSanitizer report breakpoint hit. Use 'thread "871"info -s' to get extended information about the "872"report.\n");873874return true; // Return true to stop the target875} else876return false; // Let target run877}878879const RegularExpression &880InstrumentationRuntimeTSan::GetPatternForRuntimeLibrary() {881static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_"));882return regex;883}884885bool InstrumentationRuntimeTSan::CheckIfRuntimeIsValid(886const lldb::ModuleSP module_sp) {887static ConstString g_tsan_get_current_report("__tsan_get_current_report");888const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(889g_tsan_get_current_report, lldb::eSymbolTypeAny);890return symbol != nullptr;891}892893void InstrumentationRuntimeTSan::Activate() {894if (IsActive())895return;896897ProcessSP process_sp = GetProcessSP();898if (!process_sp)899return;900901ConstString symbol_name("__tsan_on_report");902const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(903symbol_name, eSymbolTypeCode);904905if (symbol == nullptr)906return;907908if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())909return;910911Target &target = process_sp->GetTarget();912addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);913914if (symbol_address == LLDB_INVALID_ADDRESS)915return;916917const bool internal = true;918const bool hardware = false;919const bool sync = false;920Breakpoint *breakpoint =921process_sp->GetTarget()922.CreateBreakpoint(symbol_address, internal, hardware)923.get();924breakpoint->SetCallback(InstrumentationRuntimeTSan::NotifyBreakpointHit, this,925sync);926breakpoint->SetBreakpointKind("thread-sanitizer-report");927SetBreakpointID(breakpoint->GetID());928929SetActive(true);930}931932void InstrumentationRuntimeTSan::Deactivate() {933if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {934ProcessSP process_sp = GetProcessSP();935if (process_sp) {936process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());937SetBreakpointID(LLDB_INVALID_BREAK_ID);938}939}940SetActive(false);941}942static std::string GenerateThreadName(const std::string &path,943StructuredData::Object *o,944StructuredData::ObjectSP main_info) {945std::string result = "additional information";946947if (path == "mops") {948size_t size =949o->GetObjectForDotSeparatedPath("size")->GetUnsignedIntegerValue();950tid_t thread_id =951o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue();952bool is_write =953o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue();954bool is_atomic =955o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue();956addr_t addr =957o->GetObjectForDotSeparatedPath("address")->GetUnsignedIntegerValue();958959std::string addr_string = Sprintf(" at 0x%llx", addr);960961if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same")962->GetBooleanValue()) {963addr_string = "";964}965966if (main_info->GetObjectForDotSeparatedPath("issue_type")967->GetStringValue() == "external-race") {968result = Sprintf("%s access by thread %d",969is_write ? "mutating" : "read-only", thread_id);970} else if (main_info->GetObjectForDotSeparatedPath("issue_type")971->GetStringValue() == "swift-access-race") {972result = Sprintf("modifying access by thread %d", thread_id);973} else {974result = Sprintf("%s%s of size %zu%s by thread %" PRIu64,975is_atomic ? "atomic " : "", is_write ? "write" : "read",976size, addr_string.c_str(), thread_id);977}978}979980if (path == "threads") {981tid_t thread_id =982o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue();983result = Sprintf("Thread %zu created", thread_id);984}985986if (path == "locs") {987std::string type = std::string(988o->GetAsDictionary()->GetValueForKey("type")->GetStringValue());989tid_t thread_id =990o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue();991int fd = o->GetObjectForDotSeparatedPath("file_descriptor")992->GetSignedIntegerValue();993if (type == "heap") {994result = Sprintf("Heap block allocated by thread %" PRIu64, thread_id);995} else if (type == "fd") {996result = Sprintf("File descriptor %d created by thread %" PRIu64, fd,997thread_id);998}999}10001001if (path == "mutexes") {1002int mutex_id =1003o->GetObjectForDotSeparatedPath("mutex_id")->GetSignedIntegerValue();10041005result = Sprintf("Mutex M%d created", mutex_id);1006}10071008if (path == "stacks") {1009tid_t thread_id =1010o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue();1011result = Sprintf("Thread %" PRIu64, thread_id);1012}10131014result[0] = toupper(result[0]);10151016return result;1017}10181019static void AddThreadsForPath(const std::string &path,1020ThreadCollectionSP threads, ProcessSP process_sp,1021StructuredData::ObjectSP info) {1022info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach(1023[process_sp, threads, path, info](StructuredData::Object *o) -> bool {1024std::vector<lldb::addr_t> pcs;1025o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach(1026[&pcs](StructuredData::Object *pc) -> bool {1027pcs.push_back(pc->GetUnsignedIntegerValue());1028return true;1029});10301031if (pcs.size() == 0)1032return true;10331034StructuredData::ObjectSP thread_id_obj =1035o->GetObjectForDotSeparatedPath("thread_os_id");1036tid_t tid =1037thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0;10381039ThreadSP new_thread_sp =1040std::make_shared<HistoryThread>(*process_sp, tid, pcs);1041new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str());10421043// Save this in the Process' ExtendedThreadList so a strong pointer1044// retains the object1045process_sp->GetExtendedThreadList().AddThread(new_thread_sp);1046threads->AddThread(new_thread_sp);10471048return true;1049});1050}10511052lldb::ThreadCollectionSP1053InstrumentationRuntimeTSan::GetBacktracesFromExtendedStopInfo(1054StructuredData::ObjectSP info) {10551056ThreadCollectionSP threads = std::make_shared<ThreadCollection>();10571058if (info->GetObjectForDotSeparatedPath("instrumentation_class")1059->GetStringValue() != "ThreadSanitizer")1060return threads;10611062ProcessSP process_sp = GetProcessSP();10631064AddThreadsForPath("stacks", threads, process_sp, info);1065AddThreadsForPath("mops", threads, process_sp, info);1066AddThreadsForPath("locs", threads, process_sp, info);1067AddThreadsForPath("mutexes", threads, process_sp, info);1068AddThreadsForPath("threads", threads, process_sp, info);10691070return threads;1071}107210731074