202 lines · cpp
1//===-- MemoryHistoryASan.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "MemoryHistoryASan.h"10 11#include "lldb/Symbol/SymbolContext.h"12#include "lldb/Target/MemoryHistory.h"13 14#include "Plugins/InstrumentationRuntime/Utility/Utility.h"15#include "Plugins/Process/Utility/HistoryThread.h"16#include "lldb/Core/Debugger.h"17#include "lldb/Core/Module.h"18#include "lldb/Core/PluginInterface.h"19#include "lldb/Core/PluginManager.h"20#include "lldb/Expression/UserExpression.h"21#include "lldb/Target/ExecutionContext.h"22#include "lldb/Target/Target.h"23#include "lldb/Target/Thread.h"24#include "lldb/Target/ThreadList.h"25#include "lldb/ValueObject/ValueObject.h"26#include "lldb/lldb-private.h"27 28#include <sstream>29 30using namespace lldb;31using namespace lldb_private;32 33LLDB_PLUGIN_DEFINE(MemoryHistoryASan)34 35MemoryHistorySP MemoryHistoryASan::CreateInstance(const ProcessSP &process_sp) {36 if (!process_sp.get())37 return nullptr;38 39 Target &target = process_sp->GetTarget();40 41 for (ModuleSP module_sp : target.GetImages().Modules()) {42 const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(43 ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);44 45 if (symbol != nullptr)46 return MemoryHistorySP(new MemoryHistoryASan(process_sp));47 }48 49 return MemoryHistorySP();50}51 52void MemoryHistoryASan::Initialize() {53 PluginManager::RegisterPlugin(54 GetPluginNameStatic(), "ASan memory history provider.", CreateInstance);55}56 57void MemoryHistoryASan::Terminate() {58 PluginManager::UnregisterPlugin(CreateInstance);59}60 61MemoryHistoryASan::MemoryHistoryASan(const ProcessSP &process_sp) {62 if (process_sp)63 m_process_wp = process_sp;64}65 66const char *memory_history_asan_command_prefix = R"(67 extern "C"68 {69 size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size, int *thread_id);70 size_t __asan_get_free_stack(void *addr, void **trace, size_t size, int *thread_id);71 }72)";73 74const char *memory_history_asan_command_format =75 R"(76 struct {77 void *alloc_trace[256];78 size_t alloc_count;79 int alloc_tid;80 81 void *free_trace[256];82 size_t free_count;83 int free_tid;84 } t;85 86 t.alloc_count = __asan_get_alloc_stack((void *)0x%)" PRIx6487 R"(, t.alloc_trace, 256, &t.alloc_tid);88 t.free_count = __asan_get_free_stack((void *)0x%)" PRIx6489 R"(, t.free_trace, 256, &t.free_tid);90 91 t;92)";93 94static void CreateHistoryThreadFromValueObject(95 ProcessSP process_sp, ValueObjectSP return_value_sp, HistoryPCType pc_type,96 const char *type, const char *thread_name, HistoryThreads &result) {97 std::string count_path = "." + std::string(type) + "_count";98 std::string tid_path = "." + std::string(type) + "_tid";99 std::string trace_path = "." + std::string(type) + "_trace";100 101 ValueObjectSP count_sp =102 return_value_sp->GetValueForExpressionPath(count_path.c_str());103 ValueObjectSP tid_sp =104 return_value_sp->GetValueForExpressionPath(tid_path.c_str());105 106 if (!count_sp || !tid_sp)107 return;108 109 int count = count_sp->GetValueAsUnsigned(0);110 lldb::tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1;111 112 if (count <= 0)113 return;114 115 ValueObjectSP trace_sp =116 return_value_sp->GetValueForExpressionPath(trace_path.c_str());117 118 if (!trace_sp)119 return;120 121 std::vector<lldb::addr_t> pcs;122 for (int i = 0; i < count; i++) {123 addr_t pc = trace_sp->GetChildAtIndex(i)->GetValueAsUnsigned(0);124 if (pc == 0 || pc == 1 || pc == LLDB_INVALID_ADDRESS)125 continue;126 pcs.push_back(pc);127 }128 129 HistoryThread *history_thread =130 new HistoryThread(*process_sp, tid, pcs, pc_type);131 ThreadSP new_thread_sp(history_thread);132 std::ostringstream thread_name_with_number;133 thread_name_with_number << thread_name << " Thread " << tid;134 history_thread->SetThreadName(thread_name_with_number.str().c_str());135 // Save this in the Process' ExtendedThreadList so a strong pointer retains136 // the object137 process_sp->GetExtendedThreadList().AddThread(new_thread_sp);138 result.push_back(new_thread_sp);139}140 141HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {142 HistoryThreads result;143 144 ProcessSP process_sp = m_process_wp.lock();145 if (!process_sp)146 return result;147 148 ThreadSP thread_sp =149 process_sp->GetThreadList().GetExpressionExecutionThread();150 if (!thread_sp)151 return result;152 153 StackFrameSP frame_sp =154 thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);155 if (!frame_sp)156 return result;157 158 ExecutionContext exe_ctx(frame_sp);159 ValueObjectSP return_value_sp;160 StreamString expr;161 expr.Printf(memory_history_asan_command_format, address, address);162 163 EvaluateExpressionOptions options;164 options.SetUnwindOnError(true);165 options.SetTryAllThreads(true);166 options.SetStopOthers(true);167 options.SetIgnoreBreakpoints(true);168 options.SetTimeout(process_sp->GetUtilityExpressionTimeout());169 options.SetPrefix(memory_history_asan_command_prefix);170 options.SetAutoApplyFixIts(false);171 options.SetLanguage(eLanguageTypeObjC_plus_plus);172 173 auto [m, pc_type] = GetPreferredAsanModule(process_sp->GetTarget());174 if (m) {175 SymbolContextList sc_list;176 sc_list.Append(SymbolContext(std::move(m)));177 options.SetPreferredSymbolContexts(std::move(sc_list));178 }179 180 ExpressionResults expr_result = UserExpression::Evaluate(181 exe_ctx, options, expr.GetString(), "", return_value_sp);182 if (expr_result != eExpressionCompleted) {183 StreamString ss;184 ss << "cannot evaluate AddressSanitizer expression:\n";185 if (return_value_sp)186 ss << return_value_sp->GetError().AsCString();187 Debugger::ReportWarning(ss.GetString().str(),188 process_sp->GetTarget().GetDebugger().GetID());189 return result;190 }191 192 if (!return_value_sp)193 return result;194 195 CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type,196 "free", "Memory deallocated by", result);197 CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type,198 "alloc", "Memory allocated by", result);199 200 return result;201}202