mirror of
https://github.com/intel/llvm.git
synced 2026-01-21 12:19:23 +08:00
[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.
This commit is contained in:
@@ -2570,6 +2570,19 @@ void PruneThreadPlans();
|
||||
/// information related to the process.
|
||||
virtual StructuredData::DictionarySP GetMetadata() { return nullptr; }
|
||||
|
||||
/// Fetch extended crash information held by the process. This will never be
|
||||
/// an empty shared pointer, it will always have a dict, though it may be
|
||||
/// empty.
|
||||
StructuredData::DictionarySP GetExtendedCrashInfoDict() {
|
||||
assert(m_crash_info_dict_sp && "We always have a valid dictionary");
|
||||
return m_crash_info_dict_sp;
|
||||
}
|
||||
|
||||
void ResetExtendedCrashInfoDict() {
|
||||
// StructuredData::Dictionary is add only, so we have to make a new one:
|
||||
m_crash_info_dict_sp.reset(new StructuredData::Dictionary());
|
||||
}
|
||||
|
||||
size_t AddImageToken(lldb::addr_t image_ptr);
|
||||
|
||||
lldb::addr_t GetImagePtrFromToken(size_t token) const;
|
||||
@@ -3186,6 +3199,10 @@ protected:
|
||||
/// Per process source file cache.
|
||||
SourceManager::SourceFileCache m_source_file_cache;
|
||||
|
||||
/// A repository for extra crash information, consulted in
|
||||
/// GetExtendedCrashInformation.
|
||||
StructuredData::DictionarySP m_crash_info_dict_sp;
|
||||
|
||||
size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size,
|
||||
uint8_t *buf) const;
|
||||
|
||||
|
||||
@@ -857,20 +857,37 @@ PlatformDarwin::ParseVersionBuildDir(llvm::StringRef dir) {
|
||||
|
||||
llvm::Expected<StructuredData::DictionarySP>
|
||||
PlatformDarwin::FetchExtendedCrashInformation(Process &process) {
|
||||
StructuredData::DictionarySP extended_crash_info =
|
||||
std::make_shared<StructuredData::Dictionary>();
|
||||
static constexpr llvm::StringLiteral crash_info_key("Crash-Info Annotations");
|
||||
static constexpr llvm::StringLiteral asi_info_key(
|
||||
"Application Specific Information");
|
||||
|
||||
StructuredData::ArraySP annotations = ExtractCrashInfoAnnotations(process);
|
||||
if (annotations && annotations->GetSize())
|
||||
extended_crash_info->AddItem("Crash-Info Annotations", annotations);
|
||||
// We cache the information we find in the process extended info dict:
|
||||
StructuredData::DictionarySP process_dict_sp =
|
||||
process.GetExtendedCrashInfoDict();
|
||||
StructuredData::Array *annotations = nullptr;
|
||||
StructuredData::ArraySP new_annotations_sp;
|
||||
if (!process_dict_sp->GetValueForKeyAsArray(crash_info_key, annotations)) {
|
||||
new_annotations_sp = ExtractCrashInfoAnnotations(process);
|
||||
if (new_annotations_sp && new_annotations_sp->GetSize()) {
|
||||
process_dict_sp->AddItem(crash_info_key, new_annotations_sp);
|
||||
annotations = new_annotations_sp.get();
|
||||
}
|
||||
}
|
||||
|
||||
StructuredData::DictionarySP app_specific_info =
|
||||
ExtractAppSpecificInfo(process);
|
||||
if (app_specific_info && app_specific_info->GetSize())
|
||||
extended_crash_info->AddItem("Application Specific Information",
|
||||
app_specific_info);
|
||||
StructuredData::Dictionary *app_specific_info;
|
||||
StructuredData::DictionarySP new_app_specific_info_sp;
|
||||
if (!process_dict_sp->GetValueForKeyAsDictionary(asi_info_key,
|
||||
app_specific_info)) {
|
||||
new_app_specific_info_sp = ExtractAppSpecificInfo(process);
|
||||
if (new_app_specific_info_sp && new_app_specific_info_sp->GetSize()) {
|
||||
process_dict_sp->AddItem(asi_info_key, new_app_specific_info_sp);
|
||||
app_specific_info = new_app_specific_info_sp.get();
|
||||
}
|
||||
}
|
||||
|
||||
return extended_crash_info->GetSize() ? extended_crash_info : nullptr;
|
||||
// Now get anything else that was in the process info dict, and add it to the
|
||||
// return here:
|
||||
return process_dict_sp->GetSize() ? process_dict_sp : nullptr;
|
||||
}
|
||||
|
||||
StructuredData::ArraySP
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
//===-- AbortWithPayloadFrameRecognizer.cpp -------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AbortWithPayloadFrameRecognizer.h"
|
||||
|
||||
#include "lldb/Core/Value.h"
|
||||
#include "lldb/Core/ValueObjectConstResult.h"
|
||||
#include "lldb/Target/ABI.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "lldb/Target/StackFrame.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
#include "lldb/Utility/LLDBLog.h"
|
||||
#include "lldb/Utility/Log.h"
|
||||
#include "lldb/Utility/StructuredData.h"
|
||||
|
||||
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
namespace lldb_private {
|
||||
void RegisterAbortWithPayloadFrameRecognizer(Process *process) {
|
||||
// There are two user-level API's that this recognizer captures,
|
||||
// abort_with_reason and abort_with_payload. But they both call the private
|
||||
// __abort_with_payload, the abort_with_reason call fills in a null payload.
|
||||
static ConstString module_name("libsystem_kernel.dylib");
|
||||
static ConstString sym_name("__abort_with_payload");
|
||||
|
||||
if (!process)
|
||||
return;
|
||||
|
||||
process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
|
||||
std::make_shared<AbortWithPayloadFrameRecognizer>(), module_name,
|
||||
sym_name, /*first_instruction_only*/ false);
|
||||
}
|
||||
|
||||
RecognizedStackFrameSP
|
||||
AbortWithPayloadFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
|
||||
// We have two jobs:
|
||||
// 1) to add the data passed to abort_with_payload to the
|
||||
// ExtraCrashInformation dictionary.
|
||||
// 2) To make up faux arguments for this frame.
|
||||
static constexpr llvm::StringLiteral namespace_key("namespace");
|
||||
static constexpr llvm::StringLiteral code_key("code");
|
||||
static constexpr llvm::StringLiteral payload_addr_key("payload_addr");
|
||||
static constexpr llvm::StringLiteral payload_size_key("payload_size");
|
||||
static constexpr llvm::StringLiteral reason_key("reason");
|
||||
static constexpr llvm::StringLiteral flags_key("flags");
|
||||
static constexpr llvm::StringLiteral info_key("abort_with_payload");
|
||||
|
||||
Log *log = GetLog(LLDBLog::SystemRuntime);
|
||||
|
||||
if (!frame_sp) {
|
||||
LLDB_LOG(log, "abort_with_payload recognizer: invalid frame.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Thread *thread = frame_sp->GetThread().get();
|
||||
if (!thread) {
|
||||
LLDB_LOG(log, "abort_with_payload recognizer: invalid thread.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Process *process = thread->GetProcess().get();
|
||||
if (!thread) {
|
||||
LLDB_LOG(log, "abort_with_payload recognizer: invalid process.");
|
||||
}
|
||||
|
||||
TypeSystemClangSP scratch_ts_sp =
|
||||
ScratchTypeSystemClang::GetForTarget(process->GetTarget());
|
||||
if (!scratch_ts_sp) {
|
||||
LLDB_LOG(log, "abort_with_payload recognizer: invalid scratch typesystem.");
|
||||
return {};
|
||||
}
|
||||
|
||||
// The abort_with_payload signature is:
|
||||
// abort_with_payload(uint32_t reason_namespace, uint64_t reason_code,
|
||||
// void* payload, uint32_t payload_size,
|
||||
// const char* reason_string, uint64_t reason_flags);
|
||||
|
||||
ValueList arg_values;
|
||||
Value input_value_32;
|
||||
Value input_value_64;
|
||||
Value input_value_void_ptr;
|
||||
Value input_value_char_ptr;
|
||||
|
||||
CompilerType clang_void_ptr_type =
|
||||
scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
|
||||
CompilerType clang_char_ptr_type =
|
||||
scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
|
||||
CompilerType clang_uint64_type =
|
||||
scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
|
||||
64);
|
||||
CompilerType clang_uint32_type =
|
||||
scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
|
||||
32);
|
||||
CompilerType clang_char_star_type =
|
||||
scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
|
||||
64);
|
||||
|
||||
input_value_32.SetValueType(Value::ValueType::Scalar);
|
||||
input_value_32.SetCompilerType(clang_uint32_type);
|
||||
input_value_64.SetValueType(Value::ValueType::Scalar);
|
||||
input_value_64.SetCompilerType(clang_uint64_type);
|
||||
input_value_void_ptr.SetValueType(Value::ValueType::Scalar);
|
||||
input_value_void_ptr.SetCompilerType(clang_void_ptr_type);
|
||||
input_value_char_ptr.SetValueType(Value::ValueType::Scalar);
|
||||
input_value_char_ptr.SetCompilerType(clang_char_ptr_type);
|
||||
|
||||
arg_values.PushValue(input_value_32);
|
||||
arg_values.PushValue(input_value_64);
|
||||
arg_values.PushValue(input_value_void_ptr);
|
||||
arg_values.PushValue(input_value_32);
|
||||
arg_values.PushValue(input_value_char_ptr);
|
||||
arg_values.PushValue(input_value_64);
|
||||
|
||||
lldb::ABISP abi_sp = process->GetABI();
|
||||
bool success = abi_sp->GetArgumentValues(*thread, arg_values);
|
||||
if (!success)
|
||||
return {};
|
||||
|
||||
Value *cur_value;
|
||||
StackFrame *frame = frame_sp.get();
|
||||
ValueObjectListSP arguments_sp = ValueObjectListSP(new ValueObjectList());
|
||||
|
||||
auto add_to_arguments = [&](llvm::StringRef name, Value *value,
|
||||
bool dynamic) {
|
||||
ValueObjectSP cur_valobj_sp =
|
||||
ValueObjectConstResult::Create(frame, *value, ConstString(name));
|
||||
cur_valobj_sp = ValueObjectRecognizerSynthesizedValue::Create(
|
||||
*cur_valobj_sp, eValueTypeVariableArgument);
|
||||
ValueObjectSP dyn_valobj_sp;
|
||||
if (dynamic) {
|
||||
dyn_valobj_sp = cur_valobj_sp->GetDynamicValue(eDynamicDontRunTarget);
|
||||
if (dyn_valobj_sp)
|
||||
cur_valobj_sp = dyn_valobj_sp;
|
||||
}
|
||||
arguments_sp->Append(cur_valobj_sp);
|
||||
};
|
||||
|
||||
// Decode the arg_values:
|
||||
|
||||
uint32_t namespace_val = 0;
|
||||
cur_value = arg_values.GetValueAtIndex(0);
|
||||
add_to_arguments(namespace_key, cur_value, false);
|
||||
namespace_val = cur_value->GetScalar().UInt(namespace_val);
|
||||
|
||||
uint32_t code_val = 0;
|
||||
cur_value = arg_values.GetValueAtIndex(1);
|
||||
add_to_arguments(code_key, cur_value, false);
|
||||
code_val = cur_value->GetScalar().UInt(code_val);
|
||||
|
||||
lldb::addr_t payload_addr = LLDB_INVALID_ADDRESS;
|
||||
cur_value = arg_values.GetValueAtIndex(2);
|
||||
add_to_arguments(payload_addr_key, cur_value, true);
|
||||
payload_addr = cur_value->GetScalar().ULongLong(payload_addr);
|
||||
|
||||
uint32_t payload_size = 0;
|
||||
cur_value = arg_values.GetValueAtIndex(3);
|
||||
add_to_arguments(payload_size_key, cur_value, false);
|
||||
payload_size = cur_value->GetScalar().UInt(payload_size);
|
||||
|
||||
lldb::addr_t reason_addr = LLDB_INVALID_ADDRESS;
|
||||
cur_value = arg_values.GetValueAtIndex(4);
|
||||
add_to_arguments(reason_key, cur_value, false);
|
||||
reason_addr = cur_value->GetScalar().ULongLong(payload_addr);
|
||||
|
||||
// For the reason string, we want the string not the address, so fetch that.
|
||||
std::string reason_string;
|
||||
Status error;
|
||||
size_t str_len =
|
||||
process->ReadCStringFromMemory(reason_addr, reason_string, error);
|
||||
if (error.Fail()) {
|
||||
// Even if we couldn't read the string, return the other data.
|
||||
LLDB_LOG(log, "Couldn't fetch reason string: {0}.", error);
|
||||
reason_string = "<error fetching reason string>";
|
||||
}
|
||||
|
||||
uint32_t flags_val = 0;
|
||||
cur_value = arg_values.GetValueAtIndex(5);
|
||||
add_to_arguments(flags_key, cur_value, false);
|
||||
flags_val = cur_value->GetScalar().UInt(flags_val);
|
||||
|
||||
// Okay, we've gotten all the argument values, now put them in a
|
||||
// StructuredData, and add that to the Process ExtraCrashInformation:
|
||||
StructuredData::DictionarySP abort_dict_sp(new StructuredData::Dictionary());
|
||||
abort_dict_sp->AddIntegerItem(namespace_key, namespace_val);
|
||||
abort_dict_sp->AddIntegerItem(code_key, code_val);
|
||||
abort_dict_sp->AddIntegerItem(payload_addr_key, payload_addr);
|
||||
abort_dict_sp->AddIntegerItem(payload_size_key, payload_size);
|
||||
abort_dict_sp->AddStringItem(reason_key, reason_string);
|
||||
abort_dict_sp->AddIntegerItem(flags_key, flags_val);
|
||||
|
||||
// This will overwrite the abort_with_payload information in the dictionary
|
||||
// already. But we can only crash on abort_with_payload once, so that
|
||||
// shouldn't matter.
|
||||
process->GetExtendedCrashInfoDict()->AddItem(info_key, abort_dict_sp);
|
||||
|
||||
return RecognizedStackFrameSP(
|
||||
new AbortWithPayloadRecognizedStackFrame(frame_sp, arguments_sp));
|
||||
}
|
||||
|
||||
AbortWithPayloadRecognizedStackFrame::AbortWithPayloadRecognizedStackFrame(
|
||||
lldb::StackFrameSP &frame_sp, ValueObjectListSP &args_sp)
|
||||
: RecognizedStackFrame() {
|
||||
m_arguments = args_sp;
|
||||
m_stop_desc = "abort with payload or reason";
|
||||
}
|
||||
|
||||
} // namespace lldb_private
|
||||
@@ -0,0 +1,38 @@
|
||||
//===-- AbortWithPayloadFrameRecognizer.h -----------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLDB_MACOSX_ABORTWITHPAYLOADFRAMERECOGNIZER_H
|
||||
#define LLDB_MACOSX_ABORTWITHPAYLOADFRAMERECOGNIZER_H
|
||||
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "lldb/Target/StackFrameRecognizer.h"
|
||||
#include "lldb/Utility/ConstString.h"
|
||||
#include "lldb/Utility/FileSpec.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace lldb_private {
|
||||
|
||||
void RegisterAbortWithPayloadFrameRecognizer(Process *process);
|
||||
|
||||
class AbortWithPayloadRecognizedStackFrame : public RecognizedStackFrame {
|
||||
public:
|
||||
AbortWithPayloadRecognizedStackFrame(lldb::StackFrameSP &frame_sp,
|
||||
lldb::ValueObjectListSP &args_sp);
|
||||
};
|
||||
|
||||
class AbortWithPayloadFrameRecognizer : public StackFrameRecognizer {
|
||||
public:
|
||||
std::string GetName() override {
|
||||
return "abort_with_payload StackFrame Recognizer";
|
||||
}
|
||||
lldb::RecognizedStackFrameSP
|
||||
RecognizeFrame(lldb::StackFrameSP frame_sp) override;
|
||||
};
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // LLDB_MACOSX_ABORTWITHPAYLOADFRAMERECOGNIZER_H
|
||||
@@ -4,6 +4,7 @@ add_lldb_library(lldbPluginSystemRuntimeMacOSX PLUGIN
|
||||
AppleGetQueuesHandler.cpp
|
||||
AppleGetThreadItemInfoHandler.cpp
|
||||
SystemRuntimeMacOSX.cpp
|
||||
AbortWithPayloadFrameRecognizer.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbBreakpoint
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "lldb/Utility/Log.h"
|
||||
#include "lldb/Utility/StreamString.h"
|
||||
|
||||
#include "AbortWithPayloadFrameRecognizer.h"
|
||||
#include "SystemRuntimeMacOSX.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -90,7 +91,10 @@ SystemRuntimeMacOSX::SystemRuntimeMacOSX(Process *process)
|
||||
m_libpthread_offsets(), m_dispatch_tsd_indexes_addr(LLDB_INVALID_ADDRESS),
|
||||
m_libdispatch_tsd_indexes(),
|
||||
m_dispatch_voucher_offsets_addr(LLDB_INVALID_ADDRESS),
|
||||
m_libdispatch_voucher_offsets() {}
|
||||
m_libdispatch_voucher_offsets() {
|
||||
|
||||
RegisterAbortWithPayloadFrameRecognizer(process);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
SystemRuntimeMacOSX::~SystemRuntimeMacOSX() { Clear(true); }
|
||||
|
||||
@@ -477,7 +477,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
|
||||
m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
|
||||
m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
|
||||
m_can_interpret_function_calls(false), m_run_thread_plan_lock(),
|
||||
m_can_jit(eCanJITDontKnow) {
|
||||
m_can_jit(eCanJITDontKnow),
|
||||
m_crash_info_dict_sp(new StructuredData::Dictionary()) {
|
||||
CheckInWithManager();
|
||||
|
||||
Log *log = GetLog(LLDBLog::Object);
|
||||
@@ -3263,6 +3264,10 @@ Status Process::PrivateResume() {
|
||||
// If signals handing status changed we might want to update our signal
|
||||
// filters before resuming.
|
||||
UpdateAutomaticSignalFiltering();
|
||||
// Clear any crash info we accumulated for this stop, but don't do so if we
|
||||
// are running functions; we don't want to wipe out the real stop's info.
|
||||
if (!GetModID().IsLastResumeForUserExpression())
|
||||
ResetExtendedCrashInfoDict();
|
||||
|
||||
Status error(WillResume());
|
||||
// Tell the process it is about to resume before the thread list
|
||||
|
||||
4
lldb/test/API/macosx/abort_with_payload/Makefile
Normal file
4
lldb/test/API/macosx/abort_with_payload/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
C_SOURCES := main.c
|
||||
CFLAGS_EXTRAS := -std=c99
|
||||
|
||||
include Makefile.rules
|
||||
214
lldb/test/API/macosx/abort_with_payload/TestAbortWithPayload.py
Normal file
214
lldb/test/API/macosx/abort_with_payload/TestAbortWithPayload.py
Normal file
@@ -0,0 +1,214 @@
|
||||
"""
|
||||
Test that the FrameRecognizer for __abort_with_payload
|
||||
works properly
|
||||
"""
|
||||
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
import lldbsuite.test.lldbutil as lldbutil
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
||||
|
||||
class TestAbortWithPayload(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
@skipUnlessDarwin
|
||||
def test_abort_with_payload(self):
|
||||
"""There can be many tests in a test case - describe this test here."""
|
||||
self.build()
|
||||
self.abort_with_test(True)
|
||||
|
||||
@skipUnlessDarwin
|
||||
def test_abort_with_reason(self):
|
||||
"""There can be many tests in a test case - describe this test here."""
|
||||
self.build()
|
||||
self.abort_with_test(False)
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
self.main_source_file = lldb.SBFileSpec("main.c")
|
||||
|
||||
def abort_with_test(self, with_payload):
|
||||
"""If with_payload is True, we test the abort_with_payload call,
|
||||
if false, we test abort_with_reason."""
|
||||
launch_info = lldb.SBLaunchInfo([])
|
||||
if not with_payload:
|
||||
launch_info.SetArguments(["use_reason"], True)
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
|
||||
self,
|
||||
"Stop here before abort",
|
||||
self.main_source_file,
|
||||
launch_info=launch_info,
|
||||
)
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
payload_str_var = frame.FindVariable("payload_string")
|
||||
self.assertSuccess(payload_str_var.GetError(), "Got payload string var")
|
||||
payload_var_addr = payload_str_var.unsigned
|
||||
|
||||
payload_size_var = frame.FindVariable("payload_string_len")
|
||||
self.assertSuccess(payload_size_var.GetError(), "Got payload string len var")
|
||||
payload_size_val = payload_size_var.unsigned
|
||||
|
||||
# Not let it run to crash:
|
||||
process.Continue()
|
||||
|
||||
# At this point we should have stopped at the internal function.
|
||||
# Make sure we selected the right thread:
|
||||
sel_thread = process.GetSelectedThread()
|
||||
self.assertEqual(thread, sel_thread, "Selected the original thread")
|
||||
# Make sure the stop reason is right:
|
||||
self.assertEqual(
|
||||
thread.GetStopDescription(100),
|
||||
"abort with payload or reason",
|
||||
"Description was right",
|
||||
)
|
||||
frame_0 = thread.frames[0]
|
||||
self.assertEqual(frame_0.name, "__abort_with_payload", "Frame 0 was right")
|
||||
|
||||
# Now check the recognized argument values and the ExtendedCrashInformation version:
|
||||
options = lldb.SBVariablesOptions()
|
||||
options.SetIncludeRecognizedArguments(True)
|
||||
options.SetIncludeArguments(False)
|
||||
options.SetIncludeLocals(False)
|
||||
options.SetIncludeStatics(False)
|
||||
options.SetIncludeRuntimeSupportValues(False)
|
||||
|
||||
arguments = frame_0.GetVariables(options)
|
||||
|
||||
correct_values = {
|
||||
"namespace": 5,
|
||||
"code": 100,
|
||||
"payload_addr": payload_var_addr,
|
||||
"payload_size": payload_size_val,
|
||||
"payload_string": '"This is a payload that happens to be a string"',
|
||||
"reason_string": '"This is the reason string"',
|
||||
"reason_no_quote": "This is the reason string",
|
||||
"flags": 0x85,
|
||||
}
|
||||
|
||||
# First check the recognized argument values:
|
||||
self.assertEqual(len(arguments), 6, "Got all six values")
|
||||
self.assertEqual(arguments[0].name, "namespace")
|
||||
self.assertEqual(
|
||||
arguments[0].unsigned,
|
||||
correct_values["namespace"],
|
||||
"Namespace value correct",
|
||||
)
|
||||
|
||||
self.assertEqual(arguments[1].name, "code")
|
||||
self.assertEqual(
|
||||
arguments[1].unsigned, correct_values["code"], "code value correct"
|
||||
)
|
||||
|
||||
# We always stop at __abort_with_payload, regardless of whether the caller
|
||||
# was abort_with_reason or abort_with_payload or any future API that
|
||||
# funnels here. Since I don't want to have to know too much about the
|
||||
# callers, I just always report what is in the function I've
|
||||
#
|
||||
# add the payload ones if it is the payload not the reason function.
|
||||
self.assertEqual(arguments[2].name, "payload_addr")
|
||||
self.assertEqual(arguments[3].name, "payload_size")
|
||||
if with_payload:
|
||||
self.assertEqual(
|
||||
arguments[2].unsigned,
|
||||
correct_values["payload_addr"],
|
||||
"Payload matched variable address",
|
||||
)
|
||||
# We've made a payload that is a string, try to fetch that:
|
||||
char_ptr_type = target.FindFirstType("char").GetPointerType()
|
||||
self.assertTrue(char_ptr_type.IsValid(), "Got char ptr type")
|
||||
|
||||
str_val = arguments[2].Cast(char_ptr_type)
|
||||
self.assertEqual(
|
||||
str_val.summary, correct_values["payload_string"], "Got payload string"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
arguments[3].unsigned,
|
||||
correct_values["payload_size"],
|
||||
"payload size value correct",
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
arguments[2].unsigned, 0, "Got 0 payload addr for reason call"
|
||||
)
|
||||
self.assertEqual(
|
||||
arguments[3].unsigned, 0, "Got 0 payload size for reason call"
|
||||
)
|
||||
|
||||
self.assertEqual(arguments[4].name, "reason")
|
||||
self.assertEqual(
|
||||
arguments[4].summary,
|
||||
correct_values["reason_string"],
|
||||
"Reason value correct",
|
||||
)
|
||||
|
||||
self.assertEqual(arguments[5].name, "flags")
|
||||
self.assertEqual(
|
||||
arguments[5].unsigned, correct_values["flags"], "Flags value correct"
|
||||
)
|
||||
|
||||
# Also check that the same info was stored in the ExtendedCrashInformation dict:
|
||||
dict = process.GetExtendedCrashInformation()
|
||||
self.assertTrue(dict.IsValid(), "Got extended crash information dict")
|
||||
self.assertEqual(
|
||||
dict.GetType(), lldb.eStructuredDataTypeDictionary, "It is a dictionary"
|
||||
)
|
||||
|
||||
abort_dict = dict.GetValueForKey("abort_with_payload")
|
||||
self.assertTrue(abort_dict.IsValid(), "Got an abort_with_payload dict")
|
||||
self.assertEqual(
|
||||
abort_dict.GetType(),
|
||||
lldb.eStructuredDataTypeDictionary,
|
||||
"It is a dictionary",
|
||||
)
|
||||
|
||||
namespace_val = abort_dict.GetValueForKey("namespace")
|
||||
self.assertTrue(namespace_val.IsValid(), "Got a valid namespace")
|
||||
self.assertEqual(
|
||||
namespace_val.GetIntegerValue(0),
|
||||
correct_values["namespace"],
|
||||
"Namespace value correct",
|
||||
)
|
||||
|
||||
code_val = abort_dict.GetValueForKey("code")
|
||||
self.assertTrue(code_val.IsValid(), "Got a valid code")
|
||||
self.assertEqual(
|
||||
code_val.GetIntegerValue(0), correct_values["code"], "Code value correct"
|
||||
)
|
||||
|
||||
if with_payload:
|
||||
addr_val = abort_dict.GetValueForKey("payload_addr")
|
||||
self.assertTrue(addr_val.IsValid(), "Got a payload_addr")
|
||||
self.assertEqual(
|
||||
addr_val.GetIntegerValue(0),
|
||||
correct_values["payload_addr"],
|
||||
"payload_addr right in dictionary",
|
||||
)
|
||||
|
||||
size_val = abort_dict.GetValueForKey("payload_size")
|
||||
self.assertTrue(size_val.IsValid(), "Got a payload size value")
|
||||
self.assertEqual(
|
||||
size_val.GetIntegerValue(0),
|
||||
correct_values["payload_size"],
|
||||
"payload size right in dictionary",
|
||||
)
|
||||
|
||||
reason_val = abort_dict.GetValueForKey("reason")
|
||||
self.assertTrue(reason_val.IsValid(), "Got a reason key")
|
||||
self.assertEqual(
|
||||
reason_val.GetStringValue(100),
|
||||
correct_values["reason_no_quote"],
|
||||
"reason right in dictionary",
|
||||
)
|
||||
|
||||
flags_val = abort_dict.GetValueForKey("flags")
|
||||
self.assertTrue(flags_val.IsValid(), "Got a flags value")
|
||||
self.assertEqual(
|
||||
flags_val.GetIntegerValue(0),
|
||||
correct_values["flags"],
|
||||
"flags right in dictionary",
|
||||
)
|
||||
33
lldb/test/API/macosx/abort_with_payload/main.c
Normal file
33
lldb/test/API/macosx/abort_with_payload/main.c
Normal file
@@ -0,0 +1,33 @@
|
||||
// These defines are from bsd/sys/reason.h:
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code,
|
||||
void *payload, uint32_t payload_size,
|
||||
const char *reason_string,
|
||||
uint64_t reason_flags);
|
||||
|
||||
extern void abort_with_reason(uint32_t reason_namespace, uint64_t reason_code,
|
||||
const char *reason_string, uint64_t reason_flags);
|
||||
|
||||
#define OS_REASON_FLAG_FROM_USERSPACE 0x4
|
||||
#define OS_REASON_FLAG_NO_CRASH_REPORT 0x1
|
||||
#define OS_REASON_FLAG_ONE_TIME_FAILURE 0x80
|
||||
|
||||
#define MY_REASON_FLAGS \
|
||||
OS_REASON_FLAG_FROM_USERSPACE | OS_REASON_FLAG_NO_CRASH_REPORT | \
|
||||
OS_REASON_FLAG_ONE_TIME_FAILURE
|
||||
#define OS_REASON_TEST 5
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *reason_string = "This is the reason string";
|
||||
const char *payload_string = "This is a payload that happens to be a string";
|
||||
size_t payload_string_len = strlen(payload_string) + 1;
|
||||
if (argc == 1) // Stop here before abort
|
||||
abort_with_payload(OS_REASON_TEST, 100, (void *)payload_string,
|
||||
payload_string_len, reason_string, MY_REASON_FLAGS);
|
||||
else
|
||||
abort_with_reason(OS_REASON_TEST, 100, reason_string, MY_REASON_FLAGS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user