Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 1 | //===-- ProcessMinidump.cpp -------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | // Project includes |
| 11 | #include "ProcessMinidump.h" |
| 12 | #include "ThreadMinidump.h" |
| 13 | |
| 14 | // Other libraries and framework includes |
| 15 | #include "lldb/Core/DataBufferHeap.h" |
| 16 | #include "lldb/Core/Log.h" |
| 17 | #include "lldb/Core/Module.h" |
| 18 | #include "lldb/Core/ModuleSpec.h" |
| 19 | #include "lldb/Core/PluginManager.h" |
| 20 | #include "lldb/Core/Section.h" |
| 21 | #include "lldb/Core/State.h" |
| 22 | #include "lldb/Target/DynamicLoader.h" |
| 23 | #include "lldb/Target/MemoryRegionInfo.h" |
| 24 | #include "lldb/Target/Target.h" |
| 25 | #include "lldb/Target/UnixSignals.h" |
| 26 | #include "lldb/Utility/LLDBAssert.h" |
| 27 | |
| 28 | // C includes |
| 29 | // C++ includes |
| 30 | |
| 31 | using namespace lldb_private; |
| 32 | using namespace minidump; |
| 33 | |
| 34 | ConstString ProcessMinidump::GetPluginNameStatic() { |
| 35 | static ConstString g_name("minidump"); |
| 36 | return g_name; |
| 37 | } |
| 38 | |
| 39 | const char *ProcessMinidump::GetPluginDescriptionStatic() { |
| 40 | return "Minidump plug-in."; |
| 41 | } |
| 42 | |
| 43 | lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp, |
| 44 | lldb::ListenerSP listener_sp, |
| 45 | const FileSpec *crash_file) { |
| 46 | if (!crash_file) |
| 47 | return nullptr; |
| 48 | |
| 49 | lldb::ProcessSP process_sp; |
| 50 | // Read enough data for the Minidump header |
| 51 | const size_t header_size = sizeof(MinidumpHeader); |
| 52 | lldb::DataBufferSP data_sp(crash_file->MemoryMapFileContents(0, header_size)); |
| 53 | if (!data_sp) |
| 54 | return nullptr; |
| 55 | |
| 56 | // first, only try to parse the header, beacuse we need to be fast |
| 57 | llvm::ArrayRef<uint8_t> header_data(data_sp->GetBytes(), header_size); |
| 58 | const MinidumpHeader *header = MinidumpHeader::Parse(header_data); |
| 59 | |
| 60 | if (data_sp->GetByteSize() != header_size || header == nullptr) |
| 61 | return nullptr; |
| 62 | |
| 63 | lldb::DataBufferSP all_data_sp(crash_file->MemoryMapFileContents()); |
| 64 | auto minidump_parser = MinidumpParser::Create(all_data_sp); |
| 65 | // check if the parser object is valid |
Pavel Labath | 222fd13 | 2016-11-09 10:16:11 +0000 | [diff] [blame] | 66 | if (!minidump_parser) |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 67 | return nullptr; |
| 68 | |
| 69 | return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file, |
| 70 | minidump_parser.getValue()); |
| 71 | } |
| 72 | |
| 73 | bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp, |
| 74 | bool plugin_specified_by_name) { |
| 75 | return true; |
| 76 | } |
| 77 | |
| 78 | ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp, |
| 79 | lldb::ListenerSP listener_sp, |
| 80 | const FileSpec &core_file, |
| 81 | MinidumpParser minidump_parser) |
| 82 | : Process(target_sp, listener_sp), m_minidump_parser(minidump_parser), |
| 83 | m_core_file(core_file), m_is_wow64(false) {} |
| 84 | |
| 85 | ProcessMinidump::~ProcessMinidump() { |
| 86 | Clear(); |
| 87 | // We need to call finalize on the process before destroying ourselves |
| 88 | // to make sure all of the broadcaster cleanup goes as planned. If we |
| 89 | // destruct this class, then Process::~Process() might have problems |
| 90 | // trying to fully destroy the broadcaster. |
| 91 | Finalize(); |
| 92 | } |
| 93 | |
| 94 | void ProcessMinidump::Initialize() { |
| 95 | static std::once_flag g_once_flag; |
| 96 | |
| 97 | std::call_once(g_once_flag, []() { |
| 98 | PluginManager::RegisterPlugin(GetPluginNameStatic(), |
| 99 | GetPluginDescriptionStatic(), |
| 100 | ProcessMinidump::CreateInstance); |
| 101 | }); |
| 102 | } |
| 103 | |
| 104 | void ProcessMinidump::Terminate() { |
| 105 | PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); |
| 106 | } |
| 107 | |
| 108 | Error ProcessMinidump::DoLoadCore() { |
| 109 | Error error; |
| 110 | |
| 111 | m_thread_list = m_minidump_parser.GetThreads(); |
| 112 | m_active_exception = m_minidump_parser.GetExceptionStream(); |
| 113 | ReadModuleList(); |
| 114 | GetTarget().SetArchitecture(GetArchitecture()); |
| 115 | |
| 116 | llvm::Optional<lldb::pid_t> pid = m_minidump_parser.GetPid(); |
| 117 | if (!pid) { |
| 118 | error.SetErrorString("failed to parse PID"); |
| 119 | return error; |
| 120 | } |
| 121 | SetID(pid.getValue()); |
| 122 | |
| 123 | return error; |
| 124 | } |
| 125 | |
| 126 | DynamicLoader *ProcessMinidump::GetDynamicLoader() { |
| 127 | if (m_dyld_ap.get() == nullptr) |
| 128 | m_dyld_ap.reset(DynamicLoader::FindPlugin(this, nullptr)); |
| 129 | return m_dyld_ap.get(); |
| 130 | } |
| 131 | |
| 132 | ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); } |
| 133 | |
| 134 | uint32_t ProcessMinidump::GetPluginVersion() { return 1; } |
| 135 | |
Mehdi Amini | c1edf56 | 2016-11-11 04:29:25 +0000 | [diff] [blame^] | 136 | Error ProcessMinidump::DoDestroy() { return Error(); } |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 137 | |
| 138 | void ProcessMinidump::RefreshStateAfterStop() { |
| 139 | if (!m_active_exception) |
| 140 | return; |
| 141 | |
| 142 | if (m_active_exception->exception_record.exception_code == |
| 143 | MinidumpException::DumpRequested) { |
| 144 | return; |
| 145 | } |
| 146 | |
| 147 | lldb::StopInfoSP stop_info; |
| 148 | lldb::ThreadSP stop_thread; |
| 149 | |
| 150 | Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id); |
| 151 | stop_thread = Process::m_thread_list.GetSelectedThread(); |
| 152 | ArchSpec arch = GetArchitecture(); |
| 153 | |
| 154 | if (arch.GetTriple().getOS() == llvm::Triple::Linux) { |
| 155 | stop_info = StopInfo::CreateStopReasonWithSignal( |
| 156 | *stop_thread, m_active_exception->exception_record.exception_code); |
| 157 | } else { |
| 158 | std::string desc; |
| 159 | llvm::raw_string_ostream desc_stream(desc); |
| 160 | desc_stream << "Exception " |
| 161 | << llvm::format_hex( |
| 162 | m_active_exception->exception_record.exception_code, 8) |
| 163 | << " encountered at address " |
| 164 | << llvm::format_hex( |
| 165 | m_active_exception->exception_record.exception_address, |
| 166 | 8); |
| 167 | stop_info = StopInfo::CreateStopReasonWithException( |
| 168 | *stop_thread, desc_stream.str().c_str()); |
| 169 | } |
| 170 | |
| 171 | stop_thread->SetStopInfo(stop_info); |
| 172 | } |
| 173 | |
| 174 | bool ProcessMinidump::IsAlive() { return true; } |
| 175 | |
| 176 | bool ProcessMinidump::WarnBeforeDetach() const { return false; } |
| 177 | |
| 178 | size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, |
Dimitar Vlahovski | 5a19c0c | 2016-11-01 15:48:24 +0000 | [diff] [blame] | 179 | Error &error) { |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 180 | // Don't allow the caching that lldb_private::Process::ReadMemory does |
| 181 | // since we have it all cached in our dump file anyway. |
| 182 | return DoReadMemory(addr, buf, size, error); |
| 183 | } |
| 184 | |
| 185 | size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, |
Dimitar Vlahovski | 5a19c0c | 2016-11-01 15:48:24 +0000 | [diff] [blame] | 186 | Error &error) { |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 187 | |
| 188 | llvm::ArrayRef<uint8_t> mem = m_minidump_parser.GetMemory(addr, size); |
| 189 | if (mem.empty()) { |
| 190 | error.SetErrorString("could not parse memory info"); |
| 191 | return 0; |
| 192 | } |
| 193 | |
| 194 | std::memcpy(buf, mem.data(), mem.size()); |
| 195 | return mem.size(); |
| 196 | } |
| 197 | |
| 198 | ArchSpec ProcessMinidump::GetArchitecture() { |
| 199 | if (!m_is_wow64) { |
| 200 | return m_minidump_parser.GetArchitecture(); |
| 201 | } |
| 202 | |
| 203 | llvm::Triple triple; |
| 204 | triple.setVendor(llvm::Triple::VendorType::UnknownVendor); |
| 205 | triple.setArch(llvm::Triple::ArchType::x86); |
| 206 | triple.setOS(llvm::Triple::OSType::Win32); |
| 207 | return ArchSpec(triple); |
| 208 | } |
| 209 | |
Dimitar Vlahovski | 5a19c0c | 2016-11-01 15:48:24 +0000 | [diff] [blame] | 210 | Error ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr, |
| 211 | MemoryRegionInfo &range_info) { |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 212 | Error error; |
| 213 | auto info = m_minidump_parser.GetMemoryRegionInfo(load_addr); |
| 214 | if (!info) { |
| 215 | error.SetErrorString("No valid MemoryRegionInfo found!"); |
| 216 | return error; |
| 217 | } |
| 218 | range_info = info.getValue(); |
| 219 | return error; |
| 220 | } |
| 221 | |
| 222 | void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } |
| 223 | |
Dimitar Vlahovski | 5a19c0c | 2016-11-01 15:48:24 +0000 | [diff] [blame] | 224 | bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list, |
| 225 | ThreadList &new_thread_list) { |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 226 | uint32_t num_threads = 0; |
| 227 | if (m_thread_list.size() > 0) |
| 228 | num_threads = m_thread_list.size(); |
| 229 | |
| 230 | for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { |
| 231 | llvm::ArrayRef<uint8_t> context; |
| 232 | if (!m_is_wow64) |
| 233 | context = m_minidump_parser.GetThreadContext(m_thread_list[tid]); |
| 234 | else |
| 235 | context = m_minidump_parser.GetThreadContextWow64(m_thread_list[tid]); |
| 236 | |
| 237 | lldb::ThreadSP thread_sp( |
| 238 | new ThreadMinidump(*this, m_thread_list[tid], context)); |
| 239 | new_thread_list.AddThread(thread_sp); |
| 240 | } |
| 241 | return new_thread_list.GetSize(false) > 0; |
| 242 | } |
| 243 | |
| 244 | void ProcessMinidump::ReadModuleList() { |
| 245 | std::vector<const MinidumpModule *> filtered_modules = |
| 246 | m_minidump_parser.GetFilteredModuleList(); |
| 247 | |
| 248 | for (auto module : filtered_modules) { |
| 249 | llvm::Optional<std::string> name = |
| 250 | m_minidump_parser.GetMinidumpString(module->module_name_rva); |
| 251 | |
| 252 | if (!name) |
| 253 | continue; |
| 254 | |
| 255 | Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); |
| 256 | if (log) { |
Pavel Labath | eaa419c | 2016-11-02 10:29:47 +0000 | [diff] [blame] | 257 | log->Printf("ProcessMinidump::%s found module: name: %s %#010" PRIx64 |
| 258 | "-%#010" PRIx64 " size: %" PRIu32, |
| 259 | __FUNCTION__, name.getValue().c_str(), |
| 260 | uint64_t(module->base_of_image), |
| 261 | module->base_of_image + module->size_of_image, |
| 262 | uint32_t(module->size_of_image)); |
Dimitar Vlahovski | 7b18dd4 | 2016-10-31 15:35:18 +0000 | [diff] [blame] | 263 | } |
| 264 | |
| 265 | // check if the process is wow64 - a 32 bit windows process running on a |
| 266 | // 64 bit windows |
| 267 | if (llvm::StringRef(name.getValue()).endswith_lower("wow64.dll")) { |
| 268 | m_is_wow64 = true; |
| 269 | } |
| 270 | |
| 271 | const auto file_spec = FileSpec(name.getValue(), true); |
| 272 | ModuleSpec module_spec = file_spec; |
| 273 | Error error; |
| 274 | lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error); |
| 275 | if (!module_sp || error.Fail()) { |
| 276 | continue; |
| 277 | } |
| 278 | |
| 279 | if (log) { |
| 280 | log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__, |
| 281 | name.getValue().c_str()); |
| 282 | } |
| 283 | |
| 284 | bool load_addr_changed = false; |
| 285 | module_sp->SetLoadAddress(GetTarget(), module->base_of_image, false, |
| 286 | load_addr_changed); |
| 287 | } |
| 288 | } |
Dimitar Vlahovski | 5a19c0c | 2016-11-01 15:48:24 +0000 | [diff] [blame] | 289 | |
| 290 | bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) { |
| 291 | info.Clear(); |
| 292 | info.SetProcessID(GetID()); |
| 293 | info.SetArchitecture(GetArchitecture()); |
| 294 | lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); |
| 295 | if (module_sp) { |
| 296 | const bool add_exe_file_as_first_arg = false; |
| 297 | info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), |
| 298 | add_exe_file_as_first_arg); |
| 299 | } |
| 300 | return true; |
| 301 | } |