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