Skip to content

Commit 7a7cb81

Browse files
authored
[LLDB] Add a StackFrameRecognizer for the Darwin specific abort_with_payload… (#101365)
This is used by various system routines (the capabilities checker and dyld to name a few) to add extra color to an abort. This patch adds a frame recognizer so people can easily see the details, and also adds the information to the ExtendedCrashInformation dictionary. I also had to rework how the dictionary is held; previously it was created on demand, but that was inconvenient since it meant all the entries had to be produced at that same time. That didn't work for the recognizer.
1 parent e14356a commit 7a7cb81

File tree

10 files changed

+563
-14
lines changed

10 files changed

+563
-14
lines changed

lldb/include/lldb/Target/Process.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2570,6 +2570,19 @@ void PruneThreadPlans();
25702570
/// information related to the process.
25712571
virtual StructuredData::DictionarySP GetMetadata() { return nullptr; }
25722572

2573+
/// Fetch extended crash information held by the process. This will never be
2574+
/// an empty shared pointer, it will always have a dict, though it may be
2575+
/// empty.
2576+
StructuredData::DictionarySP GetExtendedCrashInfoDict() {
2577+
assert(m_crash_info_dict_sp && "We always have a valid dictionary");
2578+
return m_crash_info_dict_sp;
2579+
}
2580+
2581+
void ResetExtendedCrashInfoDict() {
2582+
// StructuredData::Dictionary is add only, so we have to make a new one:
2583+
m_crash_info_dict_sp.reset(new StructuredData::Dictionary());
2584+
}
2585+
25732586
size_t AddImageToken(lldb::addr_t image_ptr);
25742587

25752588
lldb::addr_t GetImagePtrFromToken(size_t token) const;
@@ -3186,6 +3199,10 @@ void PruneThreadPlans();
31863199
/// Per process source file cache.
31873200
SourceManager::SourceFileCache m_source_file_cache;
31883201

3202+
/// A repository for extra crash information, consulted in
3203+
/// GetExtendedCrashInformation.
3204+
StructuredData::DictionarySP m_crash_info_dict_sp;
3205+
31893206
size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size,
31903207
uint8_t *buf) const;
31913208

lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -857,20 +857,37 @@ PlatformDarwin::ParseVersionBuildDir(llvm::StringRef dir) {
857857

858858
llvm::Expected<StructuredData::DictionarySP>
859859
PlatformDarwin::FetchExtendedCrashInformation(Process &process) {
860-
StructuredData::DictionarySP extended_crash_info =
861-
std::make_shared<StructuredData::Dictionary>();
862-
863-
StructuredData::ArraySP annotations = ExtractCrashInfoAnnotations(process);
864-
if (annotations && annotations->GetSize())
865-
extended_crash_info->AddItem("Crash-Info Annotations", annotations);
860+
static constexpr llvm::StringLiteral crash_info_key("Crash-Info Annotations");
861+
static constexpr llvm::StringLiteral asi_info_key(
862+
"Application Specific Information");
863+
864+
// We cache the information we find in the process extended info dict:
865+
StructuredData::DictionarySP process_dict_sp =
866+
process.GetExtendedCrashInfoDict();
867+
StructuredData::Array *annotations = nullptr;
868+
StructuredData::ArraySP new_annotations_sp;
869+
if (!process_dict_sp->GetValueForKeyAsArray(crash_info_key, annotations)) {
870+
new_annotations_sp = ExtractCrashInfoAnnotations(process);
871+
if (new_annotations_sp && new_annotations_sp->GetSize()) {
872+
process_dict_sp->AddItem(crash_info_key, new_annotations_sp);
873+
annotations = new_annotations_sp.get();
874+
}
875+
}
866876

867-
StructuredData::DictionarySP app_specific_info =
868-
ExtractAppSpecificInfo(process);
869-
if (app_specific_info && app_specific_info->GetSize())
870-
extended_crash_info->AddItem("Application Specific Information",
871-
app_specific_info);
877+
StructuredData::Dictionary *app_specific_info;
878+
StructuredData::DictionarySP new_app_specific_info_sp;
879+
if (!process_dict_sp->GetValueForKeyAsDictionary(asi_info_key,
880+
app_specific_info)) {
881+
new_app_specific_info_sp = ExtractAppSpecificInfo(process);
882+
if (new_app_specific_info_sp && new_app_specific_info_sp->GetSize()) {
883+
process_dict_sp->AddItem(asi_info_key, new_app_specific_info_sp);
884+
app_specific_info = new_app_specific_info_sp.get();
885+
}
886+
}
872887

873-
return extended_crash_info->GetSize() ? extended_crash_info : nullptr;
888+
// Now get anything else that was in the process info dict, and add it to the
889+
// return here:
890+
return process_dict_sp->GetSize() ? process_dict_sp : nullptr;
874891
}
875892

876893
StructuredData::ArraySP
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
//===-- AbortWithPayloadFrameRecognizer.cpp -------------------------------===//
2+
//
3+
// 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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "AbortWithPayloadFrameRecognizer.h"
10+
11+
#include "lldb/Core/Value.h"
12+
#include "lldb/Core/ValueObjectConstResult.h"
13+
#include "lldb/Target/ABI.h"
14+
#include "lldb/Target/Process.h"
15+
#include "lldb/Target/StackFrame.h"
16+
#include "lldb/Target/Target.h"
17+
#include "lldb/Target/Thread.h"
18+
#include "lldb/Utility/LLDBLog.h"
19+
#include "lldb/Utility/Log.h"
20+
#include "lldb/Utility/StructuredData.h"
21+
22+
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
23+
24+
using namespace lldb;
25+
using namespace lldb_private;
26+
27+
namespace lldb_private {
28+
void RegisterAbortWithPayloadFrameRecognizer(Process *process) {
29+
// There are two user-level API's that this recognizer captures,
30+
// abort_with_reason and abort_with_payload. But they both call the private
31+
// __abort_with_payload, the abort_with_reason call fills in a null payload.
32+
static ConstString module_name("libsystem_kernel.dylib");
33+
static ConstString sym_name("__abort_with_payload");
34+
35+
if (!process)
36+
return;
37+
38+
process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
39+
std::make_shared<AbortWithPayloadFrameRecognizer>(), module_name,
40+
sym_name, /*first_instruction_only*/ false);
41+
}
42+
43+
RecognizedStackFrameSP
44+
AbortWithPayloadFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
45+
// We have two jobs:
46+
// 1) to add the data passed to abort_with_payload to the
47+
// ExtraCrashInformation dictionary.
48+
// 2) To make up faux arguments for this frame.
49+
static constexpr llvm::StringLiteral namespace_key("namespace");
50+
static constexpr llvm::StringLiteral code_key("code");
51+
static constexpr llvm::StringLiteral payload_addr_key("payload_addr");
52+
static constexpr llvm::StringLiteral payload_size_key("payload_size");
53+
static constexpr llvm::StringLiteral reason_key("reason");
54+
static constexpr llvm::StringLiteral flags_key("flags");
55+
static constexpr llvm::StringLiteral info_key("abort_with_payload");
56+
57+
Log *log = GetLog(LLDBLog::SystemRuntime);
58+
59+
if (!frame_sp) {
60+
LLDB_LOG(log, "abort_with_payload recognizer: invalid frame.");
61+
return {};
62+
}
63+
64+
Thread *thread = frame_sp->GetThread().get();
65+
if (!thread) {
66+
LLDB_LOG(log, "abort_with_payload recognizer: invalid thread.");
67+
return {};
68+
}
69+
70+
Process *process = thread->GetProcess().get();
71+
if (!thread) {
72+
LLDB_LOG(log, "abort_with_payload recognizer: invalid process.");
73+
}
74+
75+
TypeSystemClangSP scratch_ts_sp =
76+
ScratchTypeSystemClang::GetForTarget(process->GetTarget());
77+
if (!scratch_ts_sp) {
78+
LLDB_LOG(log, "abort_with_payload recognizer: invalid scratch typesystem.");
79+
return {};
80+
}
81+
82+
// The abort_with_payload signature is:
83+
// abort_with_payload(uint32_t reason_namespace, uint64_t reason_code,
84+
// void* payload, uint32_t payload_size,
85+
// const char* reason_string, uint64_t reason_flags);
86+
87+
ValueList arg_values;
88+
Value input_value_32;
89+
Value input_value_64;
90+
Value input_value_void_ptr;
91+
Value input_value_char_ptr;
92+
93+
CompilerType clang_void_ptr_type =
94+
scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
95+
CompilerType clang_char_ptr_type =
96+
scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
97+
CompilerType clang_uint64_type =
98+
scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
99+
64);
100+
CompilerType clang_uint32_type =
101+
scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
102+
32);
103+
CompilerType clang_char_star_type =
104+
scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
105+
64);
106+
107+
input_value_32.SetValueType(Value::ValueType::Scalar);
108+
input_value_32.SetCompilerType(clang_uint32_type);
109+
input_value_64.SetValueType(Value::ValueType::Scalar);
110+
input_value_64.SetCompilerType(clang_uint64_type);
111+
input_value_void_ptr.SetValueType(Value::ValueType::Scalar);
112+
input_value_void_ptr.SetCompilerType(clang_void_ptr_type);
113+
input_value_char_ptr.SetValueType(Value::ValueType::Scalar);
114+
input_value_char_ptr.SetCompilerType(clang_char_ptr_type);
115+
116+
arg_values.PushValue(input_value_32);
117+
arg_values.PushValue(input_value_64);
118+
arg_values.PushValue(input_value_void_ptr);
119+
arg_values.PushValue(input_value_32);
120+
arg_values.PushValue(input_value_char_ptr);
121+
arg_values.PushValue(input_value_64);
122+
123+
lldb::ABISP abi_sp = process->GetABI();
124+
bool success = abi_sp->GetArgumentValues(*thread, arg_values);
125+
if (!success)
126+
return {};
127+
128+
Value *cur_value;
129+
StackFrame *frame = frame_sp.get();
130+
ValueObjectListSP arguments_sp = ValueObjectListSP(new ValueObjectList());
131+
132+
auto add_to_arguments = [&](llvm::StringRef name, Value *value,
133+
bool dynamic) {
134+
ValueObjectSP cur_valobj_sp =
135+
ValueObjectConstResult::Create(frame, *value, ConstString(name));
136+
cur_valobj_sp = ValueObjectRecognizerSynthesizedValue::Create(
137+
*cur_valobj_sp, eValueTypeVariableArgument);
138+
ValueObjectSP dyn_valobj_sp;
139+
if (dynamic) {
140+
dyn_valobj_sp = cur_valobj_sp->GetDynamicValue(eDynamicDontRunTarget);
141+
if (dyn_valobj_sp)
142+
cur_valobj_sp = dyn_valobj_sp;
143+
}
144+
arguments_sp->Append(cur_valobj_sp);
145+
};
146+
147+
// Decode the arg_values:
148+
149+
uint32_t namespace_val = 0;
150+
cur_value = arg_values.GetValueAtIndex(0);
151+
add_to_arguments(namespace_key, cur_value, false);
152+
namespace_val = cur_value->GetScalar().UInt(namespace_val);
153+
154+
uint32_t code_val = 0;
155+
cur_value = arg_values.GetValueAtIndex(1);
156+
add_to_arguments(code_key, cur_value, false);
157+
code_val = cur_value->GetScalar().UInt(code_val);
158+
159+
lldb::addr_t payload_addr = LLDB_INVALID_ADDRESS;
160+
cur_value = arg_values.GetValueAtIndex(2);
161+
add_to_arguments(payload_addr_key, cur_value, true);
162+
payload_addr = cur_value->GetScalar().ULongLong(payload_addr);
163+
164+
uint32_t payload_size = 0;
165+
cur_value = arg_values.GetValueAtIndex(3);
166+
add_to_arguments(payload_size_key, cur_value, false);
167+
payload_size = cur_value->GetScalar().UInt(payload_size);
168+
169+
lldb::addr_t reason_addr = LLDB_INVALID_ADDRESS;
170+
cur_value = arg_values.GetValueAtIndex(4);
171+
add_to_arguments(reason_key, cur_value, false);
172+
reason_addr = cur_value->GetScalar().ULongLong(payload_addr);
173+
174+
// For the reason string, we want the string not the address, so fetch that.
175+
std::string reason_string;
176+
Status error;
177+
size_t str_len =
178+
process->ReadCStringFromMemory(reason_addr, reason_string, error);
179+
if (error.Fail()) {
180+
// Even if we couldn't read the string, return the other data.
181+
LLDB_LOG(log, "Couldn't fetch reason string: {0}.", error);
182+
reason_string = "<error fetching reason string>";
183+
}
184+
185+
uint32_t flags_val = 0;
186+
cur_value = arg_values.GetValueAtIndex(5);
187+
add_to_arguments(flags_key, cur_value, false);
188+
flags_val = cur_value->GetScalar().UInt(flags_val);
189+
190+
// Okay, we've gotten all the argument values, now put them in a
191+
// StructuredData, and add that to the Process ExtraCrashInformation:
192+
StructuredData::DictionarySP abort_dict_sp(new StructuredData::Dictionary());
193+
abort_dict_sp->AddIntegerItem(namespace_key, namespace_val);
194+
abort_dict_sp->AddIntegerItem(code_key, code_val);
195+
abort_dict_sp->AddIntegerItem(payload_addr_key, payload_addr);
196+
abort_dict_sp->AddIntegerItem(payload_size_key, payload_size);
197+
abort_dict_sp->AddStringItem(reason_key, reason_string);
198+
abort_dict_sp->AddIntegerItem(flags_key, flags_val);
199+
200+
// This will overwrite the abort_with_payload information in the dictionary
201+
// already. But we can only crash on abort_with_payload once, so that
202+
// shouldn't matter.
203+
process->GetExtendedCrashInfoDict()->AddItem(info_key, abort_dict_sp);
204+
205+
return RecognizedStackFrameSP(
206+
new AbortWithPayloadRecognizedStackFrame(frame_sp, arguments_sp));
207+
}
208+
209+
AbortWithPayloadRecognizedStackFrame::AbortWithPayloadRecognizedStackFrame(
210+
lldb::StackFrameSP &frame_sp, ValueObjectListSP &args_sp)
211+
: RecognizedStackFrame() {
212+
m_arguments = args_sp;
213+
m_stop_desc = "abort with payload or reason";
214+
}
215+
216+
} // namespace lldb_private
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===-- AbortWithPayloadFrameRecognizer.h -----------------------*- C++ -*-===//
2+
//
3+
// 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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
#ifndef LLDB_MACOSX_ABORTWITHPAYLOADFRAMERECOGNIZER_H
9+
#define LLDB_MACOSX_ABORTWITHPAYLOADFRAMERECOGNIZER_H
10+
11+
#include "lldb/Target/Process.h"
12+
#include "lldb/Target/StackFrameRecognizer.h"
13+
#include "lldb/Utility/ConstString.h"
14+
#include "lldb/Utility/FileSpec.h"
15+
16+
#include <tuple>
17+
18+
namespace lldb_private {
19+
20+
void RegisterAbortWithPayloadFrameRecognizer(Process *process);
21+
22+
class AbortWithPayloadRecognizedStackFrame : public RecognizedStackFrame {
23+
public:
24+
AbortWithPayloadRecognizedStackFrame(lldb::StackFrameSP &frame_sp,
25+
lldb::ValueObjectListSP &args_sp);
26+
};
27+
28+
class AbortWithPayloadFrameRecognizer : public StackFrameRecognizer {
29+
public:
30+
std::string GetName() override {
31+
return "abort_with_payload StackFrame Recognizer";
32+
}
33+
lldb::RecognizedStackFrameSP
34+
RecognizeFrame(lldb::StackFrameSP frame_sp) override;
35+
};
36+
} // namespace lldb_private
37+
38+
#endif // LLDB_MACOSX_ABORTWITHPAYLOADFRAMERECOGNIZER_H

lldb/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_lldb_library(lldbPluginSystemRuntimeMacOSX PLUGIN
44
AppleGetQueuesHandler.cpp
55
AppleGetThreadItemInfoHandler.cpp
66
SystemRuntimeMacOSX.cpp
7+
AbortWithPayloadFrameRecognizer.cpp
78

89
LINK_LIBS
910
lldbBreakpoint

lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "lldb/Utility/Log.h"
2929
#include "lldb/Utility/StreamString.h"
3030

31+
#include "AbortWithPayloadFrameRecognizer.h"
3132
#include "SystemRuntimeMacOSX.h"
3233

3334
#include <memory>
@@ -90,7 +91,10 @@ SystemRuntimeMacOSX::SystemRuntimeMacOSX(Process *process)
9091
m_libpthread_offsets(), m_dispatch_tsd_indexes_addr(LLDB_INVALID_ADDRESS),
9192
m_libdispatch_tsd_indexes(),
9293
m_dispatch_voucher_offsets_addr(LLDB_INVALID_ADDRESS),
93-
m_libdispatch_voucher_offsets() {}
94+
m_libdispatch_voucher_offsets() {
95+
96+
RegisterAbortWithPayloadFrameRecognizer(process);
97+
}
9498

9599
// Destructor
96100
SystemRuntimeMacOSX::~SystemRuntimeMacOSX() { Clear(true); }

lldb/source/Target/Process.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
477477
m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
478478
m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
479479
m_can_interpret_function_calls(false), m_run_thread_plan_lock(),
480-
m_can_jit(eCanJITDontKnow) {
480+
m_can_jit(eCanJITDontKnow),
481+
m_crash_info_dict_sp(new StructuredData::Dictionary()) {
481482
CheckInWithManager();
482483

483484
Log *log = GetLog(LLDBLog::Object);
@@ -3263,6 +3264,10 @@ Status Process::PrivateResume() {
32633264
// If signals handing status changed we might want to update our signal
32643265
// filters before resuming.
32653266
UpdateAutomaticSignalFiltering();
3267+
// Clear any crash info we accumulated for this stop, but don't do so if we
3268+
// are running functions; we don't want to wipe out the real stop's info.
3269+
if (!GetModID().IsLastResumeForUserExpression())
3270+
ResetExtendedCrashInfoDict();
32663271

32673272
Status error(WillResume());
32683273
// Tell the process it is about to resume before the thread list
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
CFLAGS_EXTRAS := -std=c99
3+
4+
include Makefile.rules

0 commit comments

Comments
 (0)