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