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