xref: /llvm-project/lldb/source/Plugins/SystemRuntime/MacOSX/AbortWithPayloadFrameRecognizer.cpp (revision b852fb1ec5fa15f0b913cc4988cbd09239b19904)
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