15ffd83dbSDimitry Andric //===-- InstrumentationRuntimeMainThreadChecker.cpp -----------------------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric 95ffd83dbSDimitry Andric #include "InstrumentationRuntimeMainThreadChecker.h" 105ffd83dbSDimitry Andric 115ffd83dbSDimitry Andric #include "Plugins/Process/Utility/HistoryThread.h" 125ffd83dbSDimitry Andric #include "lldb/Breakpoint/StoppointCallbackContext.h" 135ffd83dbSDimitry Andric #include "lldb/Core/Module.h" 145ffd83dbSDimitry Andric #include "lldb/Core/PluginManager.h" 155ffd83dbSDimitry Andric #include "lldb/Symbol/Symbol.h" 165ffd83dbSDimitry Andric #include "lldb/Symbol/SymbolContext.h" 175ffd83dbSDimitry Andric #include "lldb/Symbol/Variable.h" 185ffd83dbSDimitry Andric #include "lldb/Symbol/VariableList.h" 195ffd83dbSDimitry Andric #include "lldb/Target/InstrumentationRuntimeStopInfo.h" 205ffd83dbSDimitry Andric #include "lldb/Target/RegisterContext.h" 215ffd83dbSDimitry Andric #include "lldb/Target/SectionLoadList.h" 225ffd83dbSDimitry Andric #include "lldb/Target/StopInfo.h" 235ffd83dbSDimitry Andric #include "lldb/Target/Target.h" 245ffd83dbSDimitry Andric #include "lldb/Target/Thread.h" 255ffd83dbSDimitry Andric #include "lldb/Utility/RegularExpression.h" 265ffd83dbSDimitry Andric 275ffd83dbSDimitry Andric #include <memory> 285ffd83dbSDimitry Andric 295ffd83dbSDimitry Andric using namespace lldb; 305ffd83dbSDimitry Andric using namespace lldb_private; 315ffd83dbSDimitry Andric 325ffd83dbSDimitry Andric LLDB_PLUGIN_DEFINE(InstrumentationRuntimeMainThreadChecker) 335ffd83dbSDimitry Andric 345ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker:: 355ffd83dbSDimitry Andric ~InstrumentationRuntimeMainThreadChecker() { 365ffd83dbSDimitry Andric Deactivate(); 375ffd83dbSDimitry Andric } 385ffd83dbSDimitry Andric 395ffd83dbSDimitry Andric lldb::InstrumentationRuntimeSP 405ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::CreateInstance( 415ffd83dbSDimitry Andric const lldb::ProcessSP &process_sp) { 425ffd83dbSDimitry Andric return InstrumentationRuntimeSP( 435ffd83dbSDimitry Andric new InstrumentationRuntimeMainThreadChecker(process_sp)); 445ffd83dbSDimitry Andric } 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Initialize() { 475ffd83dbSDimitry Andric PluginManager::RegisterPlugin( 485ffd83dbSDimitry Andric GetPluginNameStatic(), 495ffd83dbSDimitry Andric "MainThreadChecker instrumentation runtime plugin.", CreateInstance, 505ffd83dbSDimitry Andric GetTypeStatic); 515ffd83dbSDimitry Andric } 525ffd83dbSDimitry Andric 535ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Terminate() { 545ffd83dbSDimitry Andric PluginManager::UnregisterPlugin(CreateInstance); 555ffd83dbSDimitry Andric } 565ffd83dbSDimitry Andric 575ffd83dbSDimitry Andric lldb_private::ConstString 585ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetPluginNameStatic() { 595ffd83dbSDimitry Andric return ConstString("MainThreadChecker"); 605ffd83dbSDimitry Andric } 615ffd83dbSDimitry Andric 625ffd83dbSDimitry Andric lldb::InstrumentationRuntimeType 635ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetTypeStatic() { 645ffd83dbSDimitry Andric return eInstrumentationRuntimeTypeMainThreadChecker; 655ffd83dbSDimitry Andric } 665ffd83dbSDimitry Andric 675ffd83dbSDimitry Andric const RegularExpression & 685ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetPatternForRuntimeLibrary() { 695ffd83dbSDimitry Andric static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib")); 705ffd83dbSDimitry Andric return regex; 715ffd83dbSDimitry Andric } 725ffd83dbSDimitry Andric 735ffd83dbSDimitry Andric bool InstrumentationRuntimeMainThreadChecker::CheckIfRuntimeIsValid( 745ffd83dbSDimitry Andric const lldb::ModuleSP module_sp) { 755ffd83dbSDimitry Andric static ConstString test_sym("__main_thread_checker_on_report"); 765ffd83dbSDimitry Andric const Symbol *symbol = 775ffd83dbSDimitry Andric module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny); 785ffd83dbSDimitry Andric return symbol != nullptr; 795ffd83dbSDimitry Andric } 805ffd83dbSDimitry Andric 815ffd83dbSDimitry Andric StructuredData::ObjectSP 825ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::RetrieveReportData( 835ffd83dbSDimitry Andric ExecutionContextRef exe_ctx_ref) { 845ffd83dbSDimitry Andric ProcessSP process_sp = GetProcessSP(); 855ffd83dbSDimitry Andric if (!process_sp) 865ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 875ffd83dbSDimitry Andric 885ffd83dbSDimitry Andric ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); 895ffd83dbSDimitry Andric StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); 905ffd83dbSDimitry Andric ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 915ffd83dbSDimitry Andric Target &target = process_sp->GetTarget(); 925ffd83dbSDimitry Andric 935ffd83dbSDimitry Andric if (!frame_sp) 945ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 955ffd83dbSDimitry Andric 965ffd83dbSDimitry Andric RegisterContextSP regctx_sp = frame_sp->GetRegisterContext(); 975ffd83dbSDimitry Andric if (!regctx_sp) 985ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 995ffd83dbSDimitry Andric 1005ffd83dbSDimitry Andric const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1"); 1015ffd83dbSDimitry Andric if (!reginfo) 1025ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 1035ffd83dbSDimitry Andric 1045ffd83dbSDimitry Andric uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0); 1055ffd83dbSDimitry Andric if (!apiname_ptr) 1065ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 1075ffd83dbSDimitry Andric 1085ffd83dbSDimitry Andric std::string apiName = ""; 1095ffd83dbSDimitry Andric Status read_error; 1105ffd83dbSDimitry Andric target.ReadCStringFromMemory(apiname_ptr, apiName, read_error); 1115ffd83dbSDimitry Andric if (read_error.Fail()) 1125ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 1135ffd83dbSDimitry Andric 1145ffd83dbSDimitry Andric std::string className = ""; 1155ffd83dbSDimitry Andric std::string selector = ""; 1165ffd83dbSDimitry Andric if (apiName.substr(0, 2) == "-[") { 117*e8d8bef9SDimitry Andric size_t spacePos = apiName.find(' '); 1185ffd83dbSDimitry Andric if (spacePos != std::string::npos) { 1195ffd83dbSDimitry Andric className = apiName.substr(2, spacePos - 2); 1205ffd83dbSDimitry Andric selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2); 1215ffd83dbSDimitry Andric } 1225ffd83dbSDimitry Andric } 1235ffd83dbSDimitry Andric 1245ffd83dbSDimitry Andric // Gather the PCs of the user frames in the backtrace. 1255ffd83dbSDimitry Andric StructuredData::Array *trace = new StructuredData::Array(); 1265ffd83dbSDimitry Andric auto trace_sp = StructuredData::ObjectSP(trace); 1275ffd83dbSDimitry Andric StackFrameSP responsible_frame; 1285ffd83dbSDimitry Andric for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) { 1295ffd83dbSDimitry Andric StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I); 1305ffd83dbSDimitry Andric Address addr = frame->GetFrameCodeAddress(); 1315ffd83dbSDimitry Andric if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime. 1325ffd83dbSDimitry Andric continue; 1335ffd83dbSDimitry Andric 1345ffd83dbSDimitry Andric // The first non-runtime frame is responsible for the bug. 1355ffd83dbSDimitry Andric if (!responsible_frame) 1365ffd83dbSDimitry Andric responsible_frame = frame; 1375ffd83dbSDimitry Andric 1385ffd83dbSDimitry Andric // First frame in stacktrace should point to a real PC, not return address. 1395ffd83dbSDimitry Andric if (I != 0 && trace->GetSize() == 0) { 1405ffd83dbSDimitry Andric addr.Slide(-1); 1415ffd83dbSDimitry Andric } 1425ffd83dbSDimitry Andric 1435ffd83dbSDimitry Andric lldb::addr_t PC = addr.GetLoadAddress(&target); 1445ffd83dbSDimitry Andric trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC))); 1455ffd83dbSDimitry Andric } 1465ffd83dbSDimitry Andric 1475ffd83dbSDimitry Andric auto *d = new StructuredData::Dictionary(); 1485ffd83dbSDimitry Andric auto dict_sp = StructuredData::ObjectSP(d); 1495ffd83dbSDimitry Andric d->AddStringItem("instrumentation_class", "MainThreadChecker"); 1505ffd83dbSDimitry Andric d->AddStringItem("api_name", apiName); 1515ffd83dbSDimitry Andric d->AddStringItem("class_name", className); 1525ffd83dbSDimitry Andric d->AddStringItem("selector", selector); 1535ffd83dbSDimitry Andric d->AddStringItem("description", 1545ffd83dbSDimitry Andric apiName + " must be used from main thread only"); 1555ffd83dbSDimitry Andric d->AddIntegerItem("tid", thread_sp->GetIndexID()); 1565ffd83dbSDimitry Andric d->AddItem("trace", trace_sp); 1575ffd83dbSDimitry Andric return dict_sp; 1585ffd83dbSDimitry Andric } 1595ffd83dbSDimitry Andric 1605ffd83dbSDimitry Andric bool InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit( 1615ffd83dbSDimitry Andric void *baton, StoppointCallbackContext *context, user_id_t break_id, 1625ffd83dbSDimitry Andric user_id_t break_loc_id) { 1635ffd83dbSDimitry Andric assert(baton && "null baton"); 1645ffd83dbSDimitry Andric if (!baton) 1655ffd83dbSDimitry Andric return false; ///< false => resume execution. 1665ffd83dbSDimitry Andric 1675ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker *const instance = 1685ffd83dbSDimitry Andric static_cast<InstrumentationRuntimeMainThreadChecker *>(baton); 1695ffd83dbSDimitry Andric 1705ffd83dbSDimitry Andric ProcessSP process_sp = instance->GetProcessSP(); 1715ffd83dbSDimitry Andric ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); 1725ffd83dbSDimitry Andric if (!process_sp || !thread_sp || 1735ffd83dbSDimitry Andric process_sp != context->exe_ctx_ref.GetProcessSP()) 1745ffd83dbSDimitry Andric return false; 1755ffd83dbSDimitry Andric 1765ffd83dbSDimitry Andric if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) 1775ffd83dbSDimitry Andric return false; 1785ffd83dbSDimitry Andric 1795ffd83dbSDimitry Andric StructuredData::ObjectSP report = 1805ffd83dbSDimitry Andric instance->RetrieveReportData(context->exe_ctx_ref); 1815ffd83dbSDimitry Andric 1825ffd83dbSDimitry Andric if (report) { 1835ffd83dbSDimitry Andric std::string description = std::string(report->GetAsDictionary() 1845ffd83dbSDimitry Andric ->GetValueForKey("description") 1855ffd83dbSDimitry Andric ->GetAsString() 1865ffd83dbSDimitry Andric ->GetValue()); 1875ffd83dbSDimitry Andric thread_sp->SetStopInfo( 1885ffd83dbSDimitry Andric InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( 1895ffd83dbSDimitry Andric *thread_sp, description, report)); 1905ffd83dbSDimitry Andric return true; 1915ffd83dbSDimitry Andric } 1925ffd83dbSDimitry Andric 1935ffd83dbSDimitry Andric return false; 1945ffd83dbSDimitry Andric } 1955ffd83dbSDimitry Andric 1965ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Activate() { 1975ffd83dbSDimitry Andric if (IsActive()) 1985ffd83dbSDimitry Andric return; 1995ffd83dbSDimitry Andric 2005ffd83dbSDimitry Andric ProcessSP process_sp = GetProcessSP(); 2015ffd83dbSDimitry Andric if (!process_sp) 2025ffd83dbSDimitry Andric return; 2035ffd83dbSDimitry Andric 2045ffd83dbSDimitry Andric ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 2055ffd83dbSDimitry Andric 2065ffd83dbSDimitry Andric ConstString symbol_name("__main_thread_checker_on_report"); 2075ffd83dbSDimitry Andric const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType( 2085ffd83dbSDimitry Andric symbol_name, eSymbolTypeCode); 2095ffd83dbSDimitry Andric 2105ffd83dbSDimitry Andric if (symbol == nullptr) 2115ffd83dbSDimitry Andric return; 2125ffd83dbSDimitry Andric 2135ffd83dbSDimitry Andric if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) 2145ffd83dbSDimitry Andric return; 2155ffd83dbSDimitry Andric 2165ffd83dbSDimitry Andric Target &target = process_sp->GetTarget(); 2175ffd83dbSDimitry Andric addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); 2185ffd83dbSDimitry Andric 2195ffd83dbSDimitry Andric if (symbol_address == LLDB_INVALID_ADDRESS) 2205ffd83dbSDimitry Andric return; 2215ffd83dbSDimitry Andric 2225ffd83dbSDimitry Andric Breakpoint *breakpoint = 2235ffd83dbSDimitry Andric process_sp->GetTarget() 2245ffd83dbSDimitry Andric .CreateBreakpoint(symbol_address, /*internal=*/true, 2255ffd83dbSDimitry Andric /*hardware=*/false) 2265ffd83dbSDimitry Andric .get(); 2275ffd83dbSDimitry Andric breakpoint->SetCallback( 2285ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit, this, true); 2295ffd83dbSDimitry Andric breakpoint->SetBreakpointKind("main-thread-checker-report"); 2305ffd83dbSDimitry Andric SetBreakpointID(breakpoint->GetID()); 2315ffd83dbSDimitry Andric 2325ffd83dbSDimitry Andric SetActive(true); 2335ffd83dbSDimitry Andric } 2345ffd83dbSDimitry Andric 2355ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Deactivate() { 2365ffd83dbSDimitry Andric SetActive(false); 2375ffd83dbSDimitry Andric 2385ffd83dbSDimitry Andric auto BID = GetBreakpointID(); 2395ffd83dbSDimitry Andric if (BID == LLDB_INVALID_BREAK_ID) 2405ffd83dbSDimitry Andric return; 2415ffd83dbSDimitry Andric 2425ffd83dbSDimitry Andric if (ProcessSP process_sp = GetProcessSP()) { 2435ffd83dbSDimitry Andric process_sp->GetTarget().RemoveBreakpointByID(BID); 2445ffd83dbSDimitry Andric SetBreakpointID(LLDB_INVALID_BREAK_ID); 2455ffd83dbSDimitry Andric } 2465ffd83dbSDimitry Andric } 2475ffd83dbSDimitry Andric 2485ffd83dbSDimitry Andric lldb::ThreadCollectionSP 2495ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo( 2505ffd83dbSDimitry Andric StructuredData::ObjectSP info) { 2515ffd83dbSDimitry Andric ThreadCollectionSP threads; 2525ffd83dbSDimitry Andric threads = std::make_shared<ThreadCollection>(); 2535ffd83dbSDimitry Andric 2545ffd83dbSDimitry Andric ProcessSP process_sp = GetProcessSP(); 2555ffd83dbSDimitry Andric 2565ffd83dbSDimitry Andric if (info->GetObjectForDotSeparatedPath("instrumentation_class") 2575ffd83dbSDimitry Andric ->GetStringValue() != "MainThreadChecker") 2585ffd83dbSDimitry Andric return threads; 2595ffd83dbSDimitry Andric 2605ffd83dbSDimitry Andric std::vector<lldb::addr_t> PCs; 2615ffd83dbSDimitry Andric auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray(); 2625ffd83dbSDimitry Andric trace->ForEach([&PCs](StructuredData::Object *PC) -> bool { 2635ffd83dbSDimitry Andric PCs.push_back(PC->GetAsInteger()->GetValue()); 2645ffd83dbSDimitry Andric return true; 2655ffd83dbSDimitry Andric }); 2665ffd83dbSDimitry Andric 2675ffd83dbSDimitry Andric if (PCs.empty()) 2685ffd83dbSDimitry Andric return threads; 2695ffd83dbSDimitry Andric 2705ffd83dbSDimitry Andric StructuredData::ObjectSP thread_id_obj = 2715ffd83dbSDimitry Andric info->GetObjectForDotSeparatedPath("tid"); 2725ffd83dbSDimitry Andric tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; 2735ffd83dbSDimitry Andric 2745ffd83dbSDimitry Andric HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs); 2755ffd83dbSDimitry Andric ThreadSP new_thread_sp(history_thread); 2765ffd83dbSDimitry Andric 2775ffd83dbSDimitry Andric // Save this in the Process' ExtendedThreadList so a strong pointer retains 2785ffd83dbSDimitry Andric // the object 2795ffd83dbSDimitry Andric process_sp->GetExtendedThreadList().AddThread(new_thread_sp); 2805ffd83dbSDimitry Andric threads->AddThread(new_thread_sp); 2815ffd83dbSDimitry Andric 2825ffd83dbSDimitry Andric return threads; 2835ffd83dbSDimitry Andric } 284