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