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