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/Target/ABI.h" 13 #include "lldb/Target/Process.h" 14 #include "lldb/Target/StackFrame.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Target/Thread.h" 17 #include "lldb/Utility/LLDBLog.h" 18 #include "lldb/Utility/Log.h" 19 #include "lldb/Utility/StructuredData.h" 20 #include "lldb/ValueObject/ValueObjectConstResult.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, Mangled::NamePreference::ePreferDemangled, 41 /*first_instruction_only*/ false); 42 } 43 44 RecognizedStackFrameSP 45 AbortWithPayloadFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { 46 // We have two jobs: 47 // 1) to add the data passed to abort_with_payload to the 48 // ExtraCrashInformation dictionary. 49 // 2) To make up faux arguments for this frame. 50 static constexpr llvm::StringLiteral namespace_key("namespace"); 51 static constexpr llvm::StringLiteral code_key("code"); 52 static constexpr llvm::StringLiteral payload_addr_key("payload_addr"); 53 static constexpr llvm::StringLiteral payload_size_key("payload_size"); 54 static constexpr llvm::StringLiteral reason_key("reason"); 55 static constexpr llvm::StringLiteral flags_key("flags"); 56 static constexpr llvm::StringLiteral info_key("abort_with_payload"); 57 58 Log *log = GetLog(LLDBLog::SystemRuntime); 59 60 if (!frame_sp) { 61 LLDB_LOG(log, "abort_with_payload recognizer: invalid frame."); 62 return {}; 63 } 64 65 Thread *thread = frame_sp->GetThread().get(); 66 if (!thread) { 67 LLDB_LOG(log, "abort_with_payload recognizer: invalid thread."); 68 return {}; 69 } 70 71 Process *process = thread->GetProcess().get(); 72 if (!thread) { 73 LLDB_LOG(log, "abort_with_payload recognizer: invalid process."); 74 } 75 76 TypeSystemClangSP scratch_ts_sp = 77 ScratchTypeSystemClang::GetForTarget(process->GetTarget()); 78 if (!scratch_ts_sp) { 79 LLDB_LOG(log, "abort_with_payload recognizer: invalid scratch typesystem."); 80 return {}; 81 } 82 83 // The abort_with_payload signature is: 84 // abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, 85 // void* payload, uint32_t payload_size, 86 // const char* reason_string, uint64_t reason_flags); 87 88 ValueList arg_values; 89 Value input_value_32; 90 Value input_value_64; 91 Value input_value_void_ptr; 92 Value input_value_char_ptr; 93 94 CompilerType clang_void_ptr_type = 95 scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); 96 CompilerType clang_char_ptr_type = 97 scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType(); 98 CompilerType clang_uint64_type = 99 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 100 64); 101 CompilerType clang_uint32_type = 102 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 103 32); 104 CompilerType clang_char_star_type = 105 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 106 64); 107 108 input_value_32.SetValueType(Value::ValueType::Scalar); 109 input_value_32.SetCompilerType(clang_uint32_type); 110 input_value_64.SetValueType(Value::ValueType::Scalar); 111 input_value_64.SetCompilerType(clang_uint64_type); 112 input_value_void_ptr.SetValueType(Value::ValueType::Scalar); 113 input_value_void_ptr.SetCompilerType(clang_void_ptr_type); 114 input_value_char_ptr.SetValueType(Value::ValueType::Scalar); 115 input_value_char_ptr.SetCompilerType(clang_char_ptr_type); 116 117 arg_values.PushValue(input_value_32); 118 arg_values.PushValue(input_value_64); 119 arg_values.PushValue(input_value_void_ptr); 120 arg_values.PushValue(input_value_32); 121 arg_values.PushValue(input_value_char_ptr); 122 arg_values.PushValue(input_value_64); 123 124 lldb::ABISP abi_sp = process->GetABI(); 125 bool success = abi_sp->GetArgumentValues(*thread, arg_values); 126 if (!success) 127 return {}; 128 129 Value *cur_value; 130 StackFrame *frame = frame_sp.get(); 131 ValueObjectListSP arguments_sp = ValueObjectListSP(new ValueObjectList()); 132 133 auto add_to_arguments = [&](llvm::StringRef name, Value *value, 134 bool dynamic) { 135 ValueObjectSP cur_valobj_sp = 136 ValueObjectConstResult::Create(frame, *value, ConstString(name)); 137 cur_valobj_sp = ValueObjectRecognizerSynthesizedValue::Create( 138 *cur_valobj_sp, eValueTypeVariableArgument); 139 ValueObjectSP dyn_valobj_sp; 140 if (dynamic) { 141 dyn_valobj_sp = cur_valobj_sp->GetDynamicValue(eDynamicDontRunTarget); 142 if (dyn_valobj_sp) 143 cur_valobj_sp = dyn_valobj_sp; 144 } 145 arguments_sp->Append(cur_valobj_sp); 146 }; 147 148 // Decode the arg_values: 149 150 uint32_t namespace_val = 0; 151 cur_value = arg_values.GetValueAtIndex(0); 152 add_to_arguments(namespace_key, cur_value, false); 153 namespace_val = cur_value->GetScalar().UInt(namespace_val); 154 155 uint32_t code_val = 0; 156 cur_value = arg_values.GetValueAtIndex(1); 157 add_to_arguments(code_key, cur_value, false); 158 code_val = cur_value->GetScalar().UInt(code_val); 159 160 lldb::addr_t payload_addr = LLDB_INVALID_ADDRESS; 161 cur_value = arg_values.GetValueAtIndex(2); 162 add_to_arguments(payload_addr_key, cur_value, true); 163 payload_addr = cur_value->GetScalar().ULongLong(payload_addr); 164 165 uint32_t payload_size = 0; 166 cur_value = arg_values.GetValueAtIndex(3); 167 add_to_arguments(payload_size_key, cur_value, false); 168 payload_size = cur_value->GetScalar().UInt(payload_size); 169 170 lldb::addr_t reason_addr = LLDB_INVALID_ADDRESS; 171 cur_value = arg_values.GetValueAtIndex(4); 172 add_to_arguments(reason_key, cur_value, false); 173 reason_addr = cur_value->GetScalar().ULongLong(payload_addr); 174 175 // For the reason string, we want the string not the address, so fetch that. 176 std::string reason_string; 177 Status error; 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 217