blob: 6f4663b5df0b83ce9efb0ed6c65605ac4d8ca658 [file] [log] [blame]
Jonas Devliegherefc1e8552020-01-21 15:01:36 -08001//===-- InstrumentationRuntimeMainThreadChecker.cpp -------------*- C++ -*-===//
Kuba Mracekef45d8b2017-06-16 20:59:08 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Kuba Mracekef45d8b2017-06-16 20:59:08 +00006//
7//===----------------------------------------------------------------------===//
8
Jonas Devliegherefc1e8552020-01-21 15:01:36 -08009#include "InstrumentationRuntimeMainThreadChecker.h"
Kuba Mracekef45d8b2017-06-16 20:59:08 +000010
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080011#include "Plugins/Process/Utility/HistoryThread.h"
Kuba Mracekef45d8b2017-06-16 20:59:08 +000012#include "lldb/Breakpoint/StoppointCallbackContext.h"
13#include "lldb/Core/Module.h"
14#include "lldb/Core/PluginManager.h"
15#include "lldb/Symbol/Symbol.h"
16#include "lldb/Symbol/SymbolContext.h"
17#include "lldb/Symbol/Variable.h"
18#include "lldb/Symbol/VariableList.h"
19#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
20#include "lldb/Target/RegisterContext.h"
21#include "lldb/Target/SectionLoadList.h"
22#include "lldb/Target/StopInfo.h"
23#include "lldb/Target/Target.h"
24#include "lldb/Target/Thread.h"
25#include "lldb/Utility/RegularExpression.h"
Kuba Mracekef45d8b2017-06-16 20:59:08 +000026
Jonas Devlieghere796ac802019-02-11 23:13:08 +000027#include <memory>
28
Kuba Mracekef45d8b2017-06-16 20:59:08 +000029using namespace lldb;
30using namespace lldb_private;
31
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080032InstrumentationRuntimeMainThreadChecker::
33 ~InstrumentationRuntimeMainThreadChecker() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000034 Deactivate();
35}
36
37lldb::InstrumentationRuntimeSP
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080038InstrumentationRuntimeMainThreadChecker::CreateInstance(
39 const lldb::ProcessSP &process_sp) {
40 return InstrumentationRuntimeSP(
41 new InstrumentationRuntimeMainThreadChecker(process_sp));
Kuba Mracekef45d8b2017-06-16 20:59:08 +000042}
43
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080044void InstrumentationRuntimeMainThreadChecker::Initialize() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000045 PluginManager::RegisterPlugin(
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080046 GetPluginNameStatic(),
47 "MainThreadChecker instrumentation runtime plugin.", CreateInstance,
48 GetTypeStatic);
Kuba Mracekef45d8b2017-06-16 20:59:08 +000049}
50
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080051void InstrumentationRuntimeMainThreadChecker::Terminate() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000052 PluginManager::UnregisterPlugin(CreateInstance);
53}
54
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080055lldb_private::ConstString
56InstrumentationRuntimeMainThreadChecker::GetPluginNameStatic() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000057 return ConstString("MainThreadChecker");
58}
59
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080060lldb::InstrumentationRuntimeType
61InstrumentationRuntimeMainThreadChecker::GetTypeStatic() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000062 return eInstrumentationRuntimeTypeMainThreadChecker;
63}
64
65const RegularExpression &
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080066InstrumentationRuntimeMainThreadChecker::GetPatternForRuntimeLibrary() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000067 static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib"));
68 return regex;
69}
70
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080071bool InstrumentationRuntimeMainThreadChecker::CheckIfRuntimeIsValid(
Kuba Mracekef45d8b2017-06-16 20:59:08 +000072 const lldb::ModuleSP module_sp) {
73 static ConstString test_sym("__main_thread_checker_on_report");
74 const Symbol *symbol =
75 module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny);
76 return symbol != nullptr;
77}
78
79StructuredData::ObjectSP
Jonas Devliegherefc1e8552020-01-21 15:01:36 -080080InstrumentationRuntimeMainThreadChecker::RetrieveReportData(
81 ExecutionContextRef exe_ctx_ref) {
Kuba Mracekef45d8b2017-06-16 20:59:08 +000082 ProcessSP process_sp = GetProcessSP();
83 if (!process_sp)
84 return StructuredData::ObjectSP();
85
86 ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
87 StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
88 ModuleSP runtime_module_sp = GetRuntimeModuleSP();
89 Target &target = process_sp->GetTarget();
90
91 if (!frame_sp)
92 return StructuredData::ObjectSP();
93
94 RegisterContextSP regctx_sp = frame_sp->GetRegisterContext();
95 if (!regctx_sp)
96 return StructuredData::ObjectSP();
97
98 const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1");
99 if (!reginfo)
100 return StructuredData::ObjectSP();
101
102 uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0);
103 if (!apiname_ptr)
104 return StructuredData::ObjectSP();
105
106 std::string apiName = "";
107 Status read_error;
108 target.ReadCStringFromMemory(apiname_ptr, apiName, read_error);
109 if (read_error.Fail())
110 return StructuredData::ObjectSP();
111
112 std::string className = "";
113 std::string selector = "";
114 if (apiName.substr(0, 2) == "-[") {
115 size_t spacePos = apiName.find(" ");
116 if (spacePos != std::string::npos) {
117 className = apiName.substr(2, spacePos - 2);
118 selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2);
119 }
120 }
121
122 // Gather the PCs of the user frames in the backtrace.
123 StructuredData::Array *trace = new StructuredData::Array();
124 auto trace_sp = StructuredData::ObjectSP(trace);
125 StackFrameSP responsible_frame;
126 for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
127 StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I);
128 Address addr = frame->GetFrameCodeAddress();
129 if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
130 continue;
131
132 // The first non-runtime frame is responsible for the bug.
133 if (!responsible_frame)
134 responsible_frame = frame;
135
136 // First frame in stacktrace should point to a real PC, not return address.
137 if (I != 0 && trace->GetSize() == 0) {
138 addr.Slide(-1);
139 }
140
141 lldb::addr_t PC = addr.GetLoadAddress(&target);
142 trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
143 }
144
145 auto *d = new StructuredData::Dictionary();
146 auto dict_sp = StructuredData::ObjectSP(d);
147 d->AddStringItem("instrumentation_class", "MainThreadChecker");
148 d->AddStringItem("api_name", apiName);
149 d->AddStringItem("class_name", className);
150 d->AddStringItem("selector", selector);
151 d->AddStringItem("description",
Kuba Mraceke21b03d2017-07-08 05:18:19 +0000152 apiName + " must be used from main thread only");
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000153 d->AddIntegerItem("tid", thread_sp->GetIndexID());
154 d->AddItem("trace", trace_sp);
155 return dict_sp;
156}
157
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800158bool InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit(
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000159 void *baton, StoppointCallbackContext *context, user_id_t break_id,
160 user_id_t break_loc_id) {
161 assert(baton && "null baton");
162 if (!baton)
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800163 return false; ///< false => resume execution.
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000164
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800165 InstrumentationRuntimeMainThreadChecker *const instance =
166 static_cast<InstrumentationRuntimeMainThreadChecker *>(baton);
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000167
168 ProcessSP process_sp = instance->GetProcessSP();
169 ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
170 if (!process_sp || !thread_sp ||
171 process_sp != context->exe_ctx_ref.GetProcessSP())
172 return false;
173
Kuba Mracekb244c2b2017-07-13 04:35:27 +0000174 if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
175 return false;
176
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000177 StructuredData::ObjectSP report =
178 instance->RetrieveReportData(context->exe_ctx_ref);
179
180 if (report) {
181 std::string description = report->GetAsDictionary()
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800182 ->GetValueForKey("description")
183 ->GetAsString()
184 ->GetValue();
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000185 thread_sp->SetStopInfo(
186 InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
187 *thread_sp, description, report));
188 return true;
189 }
190
191 return false;
192}
193
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800194void InstrumentationRuntimeMainThreadChecker::Activate() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000195 if (IsActive())
196 return;
197
198 ProcessSP process_sp = GetProcessSP();
199 if (!process_sp)
200 return;
201
202 ModuleSP runtime_module_sp = GetRuntimeModuleSP();
203
204 ConstString symbol_name("__main_thread_checker_on_report");
205 const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
206 symbol_name, eSymbolTypeCode);
207
208 if (symbol == nullptr)
209 return;
210
211 if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
212 return;
213
214 Target &target = process_sp->GetTarget();
215 addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
216
217 if (symbol_address == LLDB_INVALID_ADDRESS)
218 return;
219
220 Breakpoint *breakpoint =
221 process_sp->GetTarget()
222 .CreateBreakpoint(symbol_address, /*internal=*/true,
223 /*hardware=*/false)
224 .get();
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800225 breakpoint->SetCallback(
226 InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit, this, true);
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000227 breakpoint->SetBreakpointKind("main-thread-checker-report");
228 SetBreakpointID(breakpoint->GetID());
229
230 SetActive(true);
231}
232
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800233void InstrumentationRuntimeMainThreadChecker::Deactivate() {
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000234 SetActive(false);
235
236 auto BID = GetBreakpointID();
237 if (BID == LLDB_INVALID_BREAK_ID)
238 return;
239
240 if (ProcessSP process_sp = GetProcessSP()) {
241 process_sp->GetTarget().RemoveBreakpointByID(BID);
242 SetBreakpointID(LLDB_INVALID_BREAK_ID);
243 }
244}
245
246lldb::ThreadCollectionSP
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800247InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo(
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000248 StructuredData::ObjectSP info) {
249 ThreadCollectionSP threads;
Jonas Devlieghere796ac802019-02-11 23:13:08 +0000250 threads = std::make_shared<ThreadCollection>();
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800251
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000252 ProcessSP process_sp = GetProcessSP();
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800253
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000254 if (info->GetObjectForDotSeparatedPath("instrumentation_class")
Jonas Devliegherefc1e8552020-01-21 15:01:36 -0800255 ->GetStringValue() != "MainThreadChecker")
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000256 return threads;
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800257
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000258 std::vector<lldb::addr_t> PCs;
259 auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
260 trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
261 PCs.push_back(PC->GetAsInteger()->GetValue());
262 return true;
263 });
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800264
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000265 if (PCs.empty())
266 return threads;
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800267
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000268 StructuredData::ObjectSP thread_id_obj =
269 info->GetObjectForDotSeparatedPath("tid");
270 tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
Alex Langford86df61c2019-06-19 21:33:44 +0000271
272 HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000273 ThreadSP new_thread_sp(history_thread);
Jonas Devlieghere3b142bc2019-11-14 14:30:11 -0800274
Adrian Prantl05097242018-04-30 16:49:04 +0000275 // Save this in the Process' ExtendedThreadList so a strong pointer retains
276 // the object
Kuba Mracekef45d8b2017-06-16 20:59:08 +0000277 process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
278 threads->AddThread(new_thread_sp);
279
280 return threads;
281}