================
@@ -0,0 +1,201 @@
+//===-- 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;
+  ConstString sym_arr[1] = {sym_name};
+
+  process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
+      std::make_shared<AbortWithPayloadFrameRecognizer>(), module_name, 
sym_arr,
+      /*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");
+
+  // We are fetching the data from registers.
+  Thread *thread = frame_sp->GetThread().get();
+  Process *process = thread->GetProcess().get();
+
+  // FIXME: Add logging for these errors
+  if (!thread)
+    return {};
+
+  TypeSystemClangSP scratch_ts_sp =
+      ScratchTypeSystemClang::GetForTarget(process->GetTarget());
+  if (!scratch_ts_sp)
+    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())
+    return {};
+
+  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 then 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 any 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";
----------------
jimingham wrote:

I can't guarantee someone didn't call abort_with_payload but the payload this 
time happened to be null.  If I really wanted to do this I'd have to crawl the 
stack here and see what the caller is.  I also don't know that there will ever 
only be two API's (with_payload and with_reason) that funnel to 
__abort_with_payload.  I'm not sure that I need to be more exact about that 
since the backtrace will show you what happened.
But "abort with reason and/or payload" might be a better string here.

https://github.com/llvm/llvm-project/pull/101365
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to