1*5ffd83dbSDimitry Andric //===-- InstrumentationRuntimeMainThreadChecker.cpp -----------------------===// 2*5ffd83dbSDimitry Andric // 3*5ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*5ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*5ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*5ffd83dbSDimitry Andric // 7*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 8*5ffd83dbSDimitry Andric 9*5ffd83dbSDimitry Andric #include "InstrumentationRuntimeMainThreadChecker.h" 10*5ffd83dbSDimitry Andric 11*5ffd83dbSDimitry Andric #include "Plugins/Process/Utility/HistoryThread.h" 12*5ffd83dbSDimitry Andric #include "lldb/Breakpoint/StoppointCallbackContext.h" 13*5ffd83dbSDimitry Andric #include "lldb/Core/Module.h" 14*5ffd83dbSDimitry Andric #include "lldb/Core/PluginManager.h" 15*5ffd83dbSDimitry Andric #include "lldb/Symbol/Symbol.h" 16*5ffd83dbSDimitry Andric #include "lldb/Symbol/SymbolContext.h" 17*5ffd83dbSDimitry Andric #include "lldb/Symbol/Variable.h" 18*5ffd83dbSDimitry Andric #include "lldb/Symbol/VariableList.h" 19*5ffd83dbSDimitry Andric #include "lldb/Target/InstrumentationRuntimeStopInfo.h" 20*5ffd83dbSDimitry Andric #include "lldb/Target/RegisterContext.h" 21*5ffd83dbSDimitry Andric #include "lldb/Target/SectionLoadList.h" 22*5ffd83dbSDimitry Andric #include "lldb/Target/StopInfo.h" 23*5ffd83dbSDimitry Andric #include "lldb/Target/Target.h" 24*5ffd83dbSDimitry Andric #include "lldb/Target/Thread.h" 25*5ffd83dbSDimitry Andric #include "lldb/Utility/RegularExpression.h" 26*5ffd83dbSDimitry Andric 27*5ffd83dbSDimitry Andric #include <memory> 28*5ffd83dbSDimitry Andric 29*5ffd83dbSDimitry Andric using namespace lldb; 30*5ffd83dbSDimitry Andric using namespace lldb_private; 31*5ffd83dbSDimitry Andric 32*5ffd83dbSDimitry Andric LLDB_PLUGIN_DEFINE(InstrumentationRuntimeMainThreadChecker) 33*5ffd83dbSDimitry Andric 34*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker:: 35*5ffd83dbSDimitry Andric ~InstrumentationRuntimeMainThreadChecker() { 36*5ffd83dbSDimitry Andric Deactivate(); 37*5ffd83dbSDimitry Andric } 38*5ffd83dbSDimitry Andric 39*5ffd83dbSDimitry Andric lldb::InstrumentationRuntimeSP 40*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::CreateInstance( 41*5ffd83dbSDimitry Andric const lldb::ProcessSP &process_sp) { 42*5ffd83dbSDimitry Andric return InstrumentationRuntimeSP( 43*5ffd83dbSDimitry Andric new InstrumentationRuntimeMainThreadChecker(process_sp)); 44*5ffd83dbSDimitry Andric } 45*5ffd83dbSDimitry Andric 46*5ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Initialize() { 47*5ffd83dbSDimitry Andric PluginManager::RegisterPlugin( 48*5ffd83dbSDimitry Andric GetPluginNameStatic(), 49*5ffd83dbSDimitry Andric "MainThreadChecker instrumentation runtime plugin.", CreateInstance, 50*5ffd83dbSDimitry Andric GetTypeStatic); 51*5ffd83dbSDimitry Andric } 52*5ffd83dbSDimitry Andric 53*5ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Terminate() { 54*5ffd83dbSDimitry Andric PluginManager::UnregisterPlugin(CreateInstance); 55*5ffd83dbSDimitry Andric } 56*5ffd83dbSDimitry Andric 57*5ffd83dbSDimitry Andric lldb_private::ConstString 58*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetPluginNameStatic() { 59*5ffd83dbSDimitry Andric return ConstString("MainThreadChecker"); 60*5ffd83dbSDimitry Andric } 61*5ffd83dbSDimitry Andric 62*5ffd83dbSDimitry Andric lldb::InstrumentationRuntimeType 63*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetTypeStatic() { 64*5ffd83dbSDimitry Andric return eInstrumentationRuntimeTypeMainThreadChecker; 65*5ffd83dbSDimitry Andric } 66*5ffd83dbSDimitry Andric 67*5ffd83dbSDimitry Andric const RegularExpression & 68*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetPatternForRuntimeLibrary() { 69*5ffd83dbSDimitry Andric static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib")); 70*5ffd83dbSDimitry Andric return regex; 71*5ffd83dbSDimitry Andric } 72*5ffd83dbSDimitry Andric 73*5ffd83dbSDimitry Andric bool InstrumentationRuntimeMainThreadChecker::CheckIfRuntimeIsValid( 74*5ffd83dbSDimitry Andric const lldb::ModuleSP module_sp) { 75*5ffd83dbSDimitry Andric static ConstString test_sym("__main_thread_checker_on_report"); 76*5ffd83dbSDimitry Andric const Symbol *symbol = 77*5ffd83dbSDimitry Andric module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny); 78*5ffd83dbSDimitry Andric return symbol != nullptr; 79*5ffd83dbSDimitry Andric } 80*5ffd83dbSDimitry Andric 81*5ffd83dbSDimitry Andric StructuredData::ObjectSP 82*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::RetrieveReportData( 83*5ffd83dbSDimitry Andric ExecutionContextRef exe_ctx_ref) { 84*5ffd83dbSDimitry Andric ProcessSP process_sp = GetProcessSP(); 85*5ffd83dbSDimitry Andric if (!process_sp) 86*5ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 87*5ffd83dbSDimitry Andric 88*5ffd83dbSDimitry Andric ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); 89*5ffd83dbSDimitry Andric StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); 90*5ffd83dbSDimitry Andric ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 91*5ffd83dbSDimitry Andric Target &target = process_sp->GetTarget(); 92*5ffd83dbSDimitry Andric 93*5ffd83dbSDimitry Andric if (!frame_sp) 94*5ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 95*5ffd83dbSDimitry Andric 96*5ffd83dbSDimitry Andric RegisterContextSP regctx_sp = frame_sp->GetRegisterContext(); 97*5ffd83dbSDimitry Andric if (!regctx_sp) 98*5ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 99*5ffd83dbSDimitry Andric 100*5ffd83dbSDimitry Andric const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1"); 101*5ffd83dbSDimitry Andric if (!reginfo) 102*5ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 103*5ffd83dbSDimitry Andric 104*5ffd83dbSDimitry Andric uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0); 105*5ffd83dbSDimitry Andric if (!apiname_ptr) 106*5ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 107*5ffd83dbSDimitry Andric 108*5ffd83dbSDimitry Andric std::string apiName = ""; 109*5ffd83dbSDimitry Andric Status read_error; 110*5ffd83dbSDimitry Andric target.ReadCStringFromMemory(apiname_ptr, apiName, read_error); 111*5ffd83dbSDimitry Andric if (read_error.Fail()) 112*5ffd83dbSDimitry Andric return StructuredData::ObjectSP(); 113*5ffd83dbSDimitry Andric 114*5ffd83dbSDimitry Andric std::string className = ""; 115*5ffd83dbSDimitry Andric std::string selector = ""; 116*5ffd83dbSDimitry Andric if (apiName.substr(0, 2) == "-[") { 117*5ffd83dbSDimitry Andric size_t spacePos = apiName.find(" "); 118*5ffd83dbSDimitry Andric if (spacePos != std::string::npos) { 119*5ffd83dbSDimitry Andric className = apiName.substr(2, spacePos - 2); 120*5ffd83dbSDimitry Andric selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2); 121*5ffd83dbSDimitry Andric } 122*5ffd83dbSDimitry Andric } 123*5ffd83dbSDimitry Andric 124*5ffd83dbSDimitry Andric // Gather the PCs of the user frames in the backtrace. 125*5ffd83dbSDimitry Andric StructuredData::Array *trace = new StructuredData::Array(); 126*5ffd83dbSDimitry Andric auto trace_sp = StructuredData::ObjectSP(trace); 127*5ffd83dbSDimitry Andric StackFrameSP responsible_frame; 128*5ffd83dbSDimitry Andric for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) { 129*5ffd83dbSDimitry Andric StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I); 130*5ffd83dbSDimitry Andric Address addr = frame->GetFrameCodeAddress(); 131*5ffd83dbSDimitry Andric if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime. 132*5ffd83dbSDimitry Andric continue; 133*5ffd83dbSDimitry Andric 134*5ffd83dbSDimitry Andric // The first non-runtime frame is responsible for the bug. 135*5ffd83dbSDimitry Andric if (!responsible_frame) 136*5ffd83dbSDimitry Andric responsible_frame = frame; 137*5ffd83dbSDimitry Andric 138*5ffd83dbSDimitry Andric // First frame in stacktrace should point to a real PC, not return address. 139*5ffd83dbSDimitry Andric if (I != 0 && trace->GetSize() == 0) { 140*5ffd83dbSDimitry Andric addr.Slide(-1); 141*5ffd83dbSDimitry Andric } 142*5ffd83dbSDimitry Andric 143*5ffd83dbSDimitry Andric lldb::addr_t PC = addr.GetLoadAddress(&target); 144*5ffd83dbSDimitry Andric trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC))); 145*5ffd83dbSDimitry Andric } 146*5ffd83dbSDimitry Andric 147*5ffd83dbSDimitry Andric auto *d = new StructuredData::Dictionary(); 148*5ffd83dbSDimitry Andric auto dict_sp = StructuredData::ObjectSP(d); 149*5ffd83dbSDimitry Andric d->AddStringItem("instrumentation_class", "MainThreadChecker"); 150*5ffd83dbSDimitry Andric d->AddStringItem("api_name", apiName); 151*5ffd83dbSDimitry Andric d->AddStringItem("class_name", className); 152*5ffd83dbSDimitry Andric d->AddStringItem("selector", selector); 153*5ffd83dbSDimitry Andric d->AddStringItem("description", 154*5ffd83dbSDimitry Andric apiName + " must be used from main thread only"); 155*5ffd83dbSDimitry Andric d->AddIntegerItem("tid", thread_sp->GetIndexID()); 156*5ffd83dbSDimitry Andric d->AddItem("trace", trace_sp); 157*5ffd83dbSDimitry Andric return dict_sp; 158*5ffd83dbSDimitry Andric } 159*5ffd83dbSDimitry Andric 160*5ffd83dbSDimitry Andric bool InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit( 161*5ffd83dbSDimitry Andric void *baton, StoppointCallbackContext *context, user_id_t break_id, 162*5ffd83dbSDimitry Andric user_id_t break_loc_id) { 163*5ffd83dbSDimitry Andric assert(baton && "null baton"); 164*5ffd83dbSDimitry Andric if (!baton) 165*5ffd83dbSDimitry Andric return false; ///< false => resume execution. 166*5ffd83dbSDimitry Andric 167*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker *const instance = 168*5ffd83dbSDimitry Andric static_cast<InstrumentationRuntimeMainThreadChecker *>(baton); 169*5ffd83dbSDimitry Andric 170*5ffd83dbSDimitry Andric ProcessSP process_sp = instance->GetProcessSP(); 171*5ffd83dbSDimitry Andric ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); 172*5ffd83dbSDimitry Andric if (!process_sp || !thread_sp || 173*5ffd83dbSDimitry Andric process_sp != context->exe_ctx_ref.GetProcessSP()) 174*5ffd83dbSDimitry Andric return false; 175*5ffd83dbSDimitry Andric 176*5ffd83dbSDimitry Andric if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) 177*5ffd83dbSDimitry Andric return false; 178*5ffd83dbSDimitry Andric 179*5ffd83dbSDimitry Andric StructuredData::ObjectSP report = 180*5ffd83dbSDimitry Andric instance->RetrieveReportData(context->exe_ctx_ref); 181*5ffd83dbSDimitry Andric 182*5ffd83dbSDimitry Andric if (report) { 183*5ffd83dbSDimitry Andric std::string description = std::string(report->GetAsDictionary() 184*5ffd83dbSDimitry Andric ->GetValueForKey("description") 185*5ffd83dbSDimitry Andric ->GetAsString() 186*5ffd83dbSDimitry Andric ->GetValue()); 187*5ffd83dbSDimitry Andric thread_sp->SetStopInfo( 188*5ffd83dbSDimitry Andric InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( 189*5ffd83dbSDimitry Andric *thread_sp, description, report)); 190*5ffd83dbSDimitry Andric return true; 191*5ffd83dbSDimitry Andric } 192*5ffd83dbSDimitry Andric 193*5ffd83dbSDimitry Andric return false; 194*5ffd83dbSDimitry Andric } 195*5ffd83dbSDimitry Andric 196*5ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Activate() { 197*5ffd83dbSDimitry Andric if (IsActive()) 198*5ffd83dbSDimitry Andric return; 199*5ffd83dbSDimitry Andric 200*5ffd83dbSDimitry Andric ProcessSP process_sp = GetProcessSP(); 201*5ffd83dbSDimitry Andric if (!process_sp) 202*5ffd83dbSDimitry Andric return; 203*5ffd83dbSDimitry Andric 204*5ffd83dbSDimitry Andric ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 205*5ffd83dbSDimitry Andric 206*5ffd83dbSDimitry Andric ConstString symbol_name("__main_thread_checker_on_report"); 207*5ffd83dbSDimitry Andric const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType( 208*5ffd83dbSDimitry Andric symbol_name, eSymbolTypeCode); 209*5ffd83dbSDimitry Andric 210*5ffd83dbSDimitry Andric if (symbol == nullptr) 211*5ffd83dbSDimitry Andric return; 212*5ffd83dbSDimitry Andric 213*5ffd83dbSDimitry Andric if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) 214*5ffd83dbSDimitry Andric return; 215*5ffd83dbSDimitry Andric 216*5ffd83dbSDimitry Andric Target &target = process_sp->GetTarget(); 217*5ffd83dbSDimitry Andric addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); 218*5ffd83dbSDimitry Andric 219*5ffd83dbSDimitry Andric if (symbol_address == LLDB_INVALID_ADDRESS) 220*5ffd83dbSDimitry Andric return; 221*5ffd83dbSDimitry Andric 222*5ffd83dbSDimitry Andric Breakpoint *breakpoint = 223*5ffd83dbSDimitry Andric process_sp->GetTarget() 224*5ffd83dbSDimitry Andric .CreateBreakpoint(symbol_address, /*internal=*/true, 225*5ffd83dbSDimitry Andric /*hardware=*/false) 226*5ffd83dbSDimitry Andric .get(); 227*5ffd83dbSDimitry Andric breakpoint->SetCallback( 228*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit, this, true); 229*5ffd83dbSDimitry Andric breakpoint->SetBreakpointKind("main-thread-checker-report"); 230*5ffd83dbSDimitry Andric SetBreakpointID(breakpoint->GetID()); 231*5ffd83dbSDimitry Andric 232*5ffd83dbSDimitry Andric SetActive(true); 233*5ffd83dbSDimitry Andric } 234*5ffd83dbSDimitry Andric 235*5ffd83dbSDimitry Andric void InstrumentationRuntimeMainThreadChecker::Deactivate() { 236*5ffd83dbSDimitry Andric SetActive(false); 237*5ffd83dbSDimitry Andric 238*5ffd83dbSDimitry Andric auto BID = GetBreakpointID(); 239*5ffd83dbSDimitry Andric if (BID == LLDB_INVALID_BREAK_ID) 240*5ffd83dbSDimitry Andric return; 241*5ffd83dbSDimitry Andric 242*5ffd83dbSDimitry Andric if (ProcessSP process_sp = GetProcessSP()) { 243*5ffd83dbSDimitry Andric process_sp->GetTarget().RemoveBreakpointByID(BID); 244*5ffd83dbSDimitry Andric SetBreakpointID(LLDB_INVALID_BREAK_ID); 245*5ffd83dbSDimitry Andric } 246*5ffd83dbSDimitry Andric } 247*5ffd83dbSDimitry Andric 248*5ffd83dbSDimitry Andric lldb::ThreadCollectionSP 249*5ffd83dbSDimitry Andric InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo( 250*5ffd83dbSDimitry Andric StructuredData::ObjectSP info) { 251*5ffd83dbSDimitry Andric ThreadCollectionSP threads; 252*5ffd83dbSDimitry Andric threads = std::make_shared<ThreadCollection>(); 253*5ffd83dbSDimitry Andric 254*5ffd83dbSDimitry Andric ProcessSP process_sp = GetProcessSP(); 255*5ffd83dbSDimitry Andric 256*5ffd83dbSDimitry Andric if (info->GetObjectForDotSeparatedPath("instrumentation_class") 257*5ffd83dbSDimitry Andric ->GetStringValue() != "MainThreadChecker") 258*5ffd83dbSDimitry Andric return threads; 259*5ffd83dbSDimitry Andric 260*5ffd83dbSDimitry Andric std::vector<lldb::addr_t> PCs; 261*5ffd83dbSDimitry Andric auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray(); 262*5ffd83dbSDimitry Andric trace->ForEach([&PCs](StructuredData::Object *PC) -> bool { 263*5ffd83dbSDimitry Andric PCs.push_back(PC->GetAsInteger()->GetValue()); 264*5ffd83dbSDimitry Andric return true; 265*5ffd83dbSDimitry Andric }); 266*5ffd83dbSDimitry Andric 267*5ffd83dbSDimitry Andric if (PCs.empty()) 268*5ffd83dbSDimitry Andric return threads; 269*5ffd83dbSDimitry Andric 270*5ffd83dbSDimitry Andric StructuredData::ObjectSP thread_id_obj = 271*5ffd83dbSDimitry Andric info->GetObjectForDotSeparatedPath("tid"); 272*5ffd83dbSDimitry Andric tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; 273*5ffd83dbSDimitry Andric 274*5ffd83dbSDimitry Andric HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs); 275*5ffd83dbSDimitry Andric ThreadSP new_thread_sp(history_thread); 276*5ffd83dbSDimitry Andric 277*5ffd83dbSDimitry Andric // Save this in the Process' ExtendedThreadList so a strong pointer retains 278*5ffd83dbSDimitry Andric // the object 279*5ffd83dbSDimitry Andric process_sp->GetExtendedThreadList().AddThread(new_thread_sp); 280*5ffd83dbSDimitry Andric threads->AddThread(new_thread_sp); 281*5ffd83dbSDimitry Andric 282*5ffd83dbSDimitry Andric return threads; 283*5ffd83dbSDimitry Andric } 284