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 |
| 66 | // skip if the Minidump file is Windows generated, because we are still |
| 67 | // work-in-progress |
| 68 | if (!minidump_parser || |
| 69 | minidump_parser->GetArchitecture().GetTriple().getOS() == |
| 70 | llvm::Triple::OSType::Win32) |
| 71 | return nullptr; |
| 72 | |
| 73 | return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file, |
| 74 | minidump_parser.getValue()); |
| 75 | } |
| 76 | |
| 77 | bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp, |
| 78 | bool plugin_specified_by_name) { |
| 79 | return true; |
| 80 | } |
| 81 | |
| 82 | ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp, |
| 83 | lldb::ListenerSP listener_sp, |
| 84 | const FileSpec &core_file, |
| 85 | MinidumpParser minidump_parser) |
| 86 | : Process(target_sp, listener_sp), m_minidump_parser(minidump_parser), |
| 87 | m_core_file(core_file), m_is_wow64(false) {} |
| 88 | |
| 89 | ProcessMinidump::~ProcessMinidump() { |
| 90 | Clear(); |
| 91 | // We need to call finalize on the process before destroying ourselves |
| 92 | // to make sure all of the broadcaster cleanup goes as planned. If we |
| 93 | // destruct this class, then Process::~Process() might have problems |
| 94 | // trying to fully destroy the broadcaster. |
| 95 | Finalize(); |
| 96 | } |
| 97 | |
| 98 | void ProcessMinidump::Initialize() { |
| 99 | static std::once_flag g_once_flag; |
| 100 | |
| 101 | std::call_once(g_once_flag, []() { |
| 102 | PluginManager::RegisterPlugin(GetPluginNameStatic(), |
| 103 | GetPluginDescriptionStatic(), |
| 104 | ProcessMinidump::CreateInstance); |
| 105 | }); |
| 106 | } |
| 107 | |
| 108 | void ProcessMinidump::Terminate() { |
| 109 | PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); |
| 110 | } |
| 111 | |
| 112 | Error ProcessMinidump::DoLoadCore() { |
| 113 | Error error; |
| 114 | |
| 115 | m_thread_list = m_minidump_parser.GetThreads(); |
| 116 | m_active_exception = m_minidump_parser.GetExceptionStream(); |
| 117 | ReadModuleList(); |
| 118 | GetTarget().SetArchitecture(GetArchitecture()); |
| 119 | |
| 120 | llvm::Optional<lldb::pid_t> pid = m_minidump_parser.GetPid(); |
| 121 | if (!pid) { |
| 122 | error.SetErrorString("failed to parse PID"); |
| 123 | return error; |
| 124 | } |
| 125 | SetID(pid.getValue()); |
| 126 | |
| 127 | return error; |
| 128 | } |
| 129 | |
| 130 | DynamicLoader *ProcessMinidump::GetDynamicLoader() { |
| 131 | if (m_dyld_ap.get() == nullptr) |
| 132 | m_dyld_ap.reset(DynamicLoader::FindPlugin(this, nullptr)); |
| 133 | return m_dyld_ap.get(); |
| 134 | } |
| 135 | |
| 136 | ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); } |
| 137 | |
| 138 | uint32_t ProcessMinidump::GetPluginVersion() { return 1; } |
| 139 | |
| 140 | Error ProcessMinidump::DoDestroy() { return Error(); } |
| 141 | |
| 142 | void ProcessMinidump::RefreshStateAfterStop() { |
| 143 | if (!m_active_exception) |
| 144 | return; |
| 145 | |
| 146 | if (m_active_exception->exception_record.exception_code == |
| 147 | MinidumpException::DumpRequested) { |
| 148 | return; |
| 149 | } |
| 150 | |
| 151 | lldb::StopInfoSP stop_info; |
| 152 | lldb::ThreadSP stop_thread; |
| 153 | |
| 154 | Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id); |
| 155 | stop_thread = Process::m_thread_list.GetSelectedThread(); |
| 156 | ArchSpec arch = GetArchitecture(); |
| 157 | |
| 158 | if (arch.GetTriple().getOS() == llvm::Triple::Linux) { |
| 159 | stop_info = StopInfo::CreateStopReasonWithSignal( |
| 160 | *stop_thread, m_active_exception->exception_record.exception_code); |
| 161 | } else { |
| 162 | std::string desc; |
| 163 | llvm::raw_string_ostream desc_stream(desc); |
| 164 | desc_stream << "Exception " |
| 165 | << llvm::format_hex( |
| 166 | m_active_exception->exception_record.exception_code, 8) |
| 167 | << " encountered at address " |
| 168 | << llvm::format_hex( |
| 169 | m_active_exception->exception_record.exception_address, |
| 170 | 8); |
| 171 | stop_info = StopInfo::CreateStopReasonWithException( |
| 172 | *stop_thread, desc_stream.str().c_str()); |
| 173 | } |
| 174 | |
| 175 | stop_thread->SetStopInfo(stop_info); |
| 176 | } |
| 177 | |
| 178 | bool ProcessMinidump::IsAlive() { return true; } |
| 179 | |
| 180 | bool ProcessMinidump::WarnBeforeDetach() const { return false; } |
| 181 | |
| 182 | size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, |
| 183 | lldb_private::Error &error) { |
| 184 | // Don't allow the caching that lldb_private::Process::ReadMemory does |
| 185 | // since we have it all cached in our dump file anyway. |
| 186 | return DoReadMemory(addr, buf, size, error); |
| 187 | } |
| 188 | |
| 189 | size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, |
| 190 | lldb_private::Error &error) { |
| 191 | |
| 192 | llvm::ArrayRef<uint8_t> mem = m_minidump_parser.GetMemory(addr, size); |
| 193 | if (mem.empty()) { |
| 194 | error.SetErrorString("could not parse memory info"); |
| 195 | return 0; |
| 196 | } |
| 197 | |
| 198 | std::memcpy(buf, mem.data(), mem.size()); |
| 199 | return mem.size(); |
| 200 | } |
| 201 | |
| 202 | ArchSpec ProcessMinidump::GetArchitecture() { |
| 203 | if (!m_is_wow64) { |
| 204 | return m_minidump_parser.GetArchitecture(); |
| 205 | } |
| 206 | |
| 207 | llvm::Triple triple; |
| 208 | triple.setVendor(llvm::Triple::VendorType::UnknownVendor); |
| 209 | triple.setArch(llvm::Triple::ArchType::x86); |
| 210 | triple.setOS(llvm::Triple::OSType::Win32); |
| 211 | return ArchSpec(triple); |
| 212 | } |
| 213 | |
| 214 | Error ProcessMinidump::GetMemoryRegionInfo( |
| 215 | lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &range_info) { |
| 216 | Error error; |
| 217 | auto info = m_minidump_parser.GetMemoryRegionInfo(load_addr); |
| 218 | if (!info) { |
| 219 | error.SetErrorString("No valid MemoryRegionInfo found!"); |
| 220 | return error; |
| 221 | } |
| 222 | range_info = info.getValue(); |
| 223 | return error; |
| 224 | } |
| 225 | |
| 226 | void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } |
| 227 | |
| 228 | bool ProcessMinidump::UpdateThreadList( |
| 229 | lldb_private::ThreadList &old_thread_list, |
| 230 | lldb_private::ThreadList &new_thread_list) { |
| 231 | uint32_t num_threads = 0; |
| 232 | if (m_thread_list.size() > 0) |
| 233 | num_threads = m_thread_list.size(); |
| 234 | |
| 235 | for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { |
| 236 | llvm::ArrayRef<uint8_t> context; |
| 237 | if (!m_is_wow64) |
| 238 | context = m_minidump_parser.GetThreadContext(m_thread_list[tid]); |
| 239 | else |
| 240 | context = m_minidump_parser.GetThreadContextWow64(m_thread_list[tid]); |
| 241 | |
| 242 | lldb::ThreadSP thread_sp( |
| 243 | new ThreadMinidump(*this, m_thread_list[tid], context)); |
| 244 | new_thread_list.AddThread(thread_sp); |
| 245 | } |
| 246 | return new_thread_list.GetSize(false) > 0; |
| 247 | } |
| 248 | |
| 249 | void ProcessMinidump::ReadModuleList() { |
| 250 | std::vector<const MinidumpModule *> filtered_modules = |
| 251 | m_minidump_parser.GetFilteredModuleList(); |
| 252 | |
| 253 | for (auto module : filtered_modules) { |
| 254 | llvm::Optional<std::string> name = |
| 255 | m_minidump_parser.GetMinidumpString(module->module_name_rva); |
| 256 | |
| 257 | if (!name) |
| 258 | continue; |
| 259 | |
| 260 | Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); |
| 261 | if (log) { |
| 262 | log->Printf( |
| 263 | "ProcessMinidump::%s found module: name: %s %#010lx-%#010lx size: %u", |
| 264 | __FUNCTION__, name.getValue().c_str(), |
| 265 | uint64_t(module->base_of_image), |
| 266 | module->base_of_image + module->size_of_image, |
| 267 | uint32_t(module->size_of_image)); |
| 268 | } |
| 269 | |
| 270 | // check if the process is wow64 - a 32 bit windows process running on a |
| 271 | // 64 bit windows |
| 272 | if (llvm::StringRef(name.getValue()).endswith_lower("wow64.dll")) { |
| 273 | m_is_wow64 = true; |
| 274 | } |
| 275 | |
| 276 | const auto file_spec = FileSpec(name.getValue(), true); |
| 277 | ModuleSpec module_spec = file_spec; |
| 278 | Error error; |
| 279 | lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error); |
| 280 | if (!module_sp || error.Fail()) { |
| 281 | continue; |
| 282 | } |
| 283 | |
| 284 | if (log) { |
| 285 | log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__, |
| 286 | name.getValue().c_str()); |
| 287 | } |
| 288 | |
| 289 | bool load_addr_changed = false; |
| 290 | module_sp->SetLoadAddress(GetTarget(), module->base_of_image, false, |
| 291 | load_addr_changed); |
| 292 | } |
| 293 | } |