| 1 | // Licensed to the .NET Foundation under one or more agreements. | 
| 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
| 3 | // See the LICENSE file in the project root for more information. | 
| 4 |  | 
| 5 | #include "createdump.h" | 
| 6 |  | 
| 7 | // This is for the PAL_VirtualUnwindOutOfProc read memory adapter. | 
| 8 | CrashInfo* g_crashInfo; | 
| 9 |  | 
| 10 | CrashInfo::CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos) : | 
| 11 |     m_ref(1), | 
| 12 |     m_pid(pid), | 
| 13 |     m_ppid(-1), | 
| 14 |     m_name(nullptr), | 
| 15 |     m_sos(sos), | 
| 16 |     m_dataTarget(dataTarget) | 
| 17 | { | 
| 18 |     g_crashInfo = this; | 
| 19 |     dataTarget->AddRef(); | 
| 20 |     m_auxvValues.fill(0); | 
| 21 | } | 
| 22 |  | 
| 23 | CrashInfo::~CrashInfo() | 
| 24 | { | 
| 25 |     if (m_name != nullptr) | 
| 26 |     { | 
| 27 |         free(m_name); | 
| 28 |     } | 
| 29 |     // Clean up the threads | 
| 30 |     for (ThreadInfo* thread : m_threads) | 
| 31 |     { | 
| 32 |         delete thread; | 
| 33 |     } | 
| 34 |     m_threads.clear(); | 
| 35 |  | 
| 36 |     // Module and other mappings have a file name to clean up. | 
| 37 |     for (const MemoryRegion& region : m_moduleMappings) | 
| 38 |     { | 
| 39 |         const_cast<MemoryRegion&>(region).Cleanup(); | 
| 40 |     } | 
| 41 |     m_moduleMappings.clear(); | 
| 42 |     for (const MemoryRegion& region : m_otherMappings) | 
| 43 |     { | 
| 44 |         const_cast<MemoryRegion&>(region).Cleanup(); | 
| 45 |     } | 
| 46 |     m_otherMappings.clear(); | 
| 47 |     m_dataTarget->Release(); | 
| 48 | } | 
| 49 |  | 
| 50 | STDMETHODIMP | 
| 51 | CrashInfo::QueryInterface( | 
| 52 |     ___in REFIID InterfaceId, | 
| 53 |     ___out PVOID* Interface) | 
| 54 | { | 
| 55 |     if (InterfaceId == IID_IUnknown || | 
| 56 |         InterfaceId == IID_ICLRDataEnumMemoryRegionsCallback) | 
| 57 |     { | 
| 58 |         *Interface = (ICLRDataEnumMemoryRegionsCallback*)this; | 
| 59 |         AddRef(); | 
| 60 |         return S_OK; | 
| 61 |     } | 
| 62 |     else | 
| 63 |     { | 
| 64 |         *Interface = nullptr; | 
| 65 |         return E_NOINTERFACE; | 
| 66 |     } | 
| 67 | } | 
| 68 |  | 
| 69 | STDMETHODIMP_(ULONG) | 
| 70 | CrashInfo::AddRef() | 
| 71 | { | 
| 72 |     LONG ref = InterlockedIncrement(&m_ref);     | 
| 73 |     return ref; | 
| 74 | } | 
| 75 |  | 
| 76 | STDMETHODIMP_(ULONG) | 
| 77 | CrashInfo::Release() | 
| 78 | { | 
| 79 |     LONG ref = InterlockedDecrement(&m_ref); | 
| 80 |     if (ref == 0) | 
| 81 |     { | 
| 82 |         delete this; | 
| 83 |     } | 
| 84 |     return ref; | 
| 85 | } | 
| 86 |  | 
| 87 | HRESULT STDMETHODCALLTYPE | 
| 88 | CrashInfo::EnumMemoryRegion(  | 
| 89 |     /* [in] */ CLRDATA_ADDRESS address, | 
| 90 |     /* [in] */ ULONG32 size) | 
| 91 | { | 
| 92 |     InsertMemoryRegion((ULONG_PTR)address, size); | 
| 93 |     return S_OK; | 
| 94 | } | 
| 95 |  | 
| 96 | // | 
| 97 | // Suspends all the threads and creating a list of them. Should be the first before  | 
| 98 | // gather any info about the process. | 
| 99 | // | 
| 100 | bool  | 
| 101 | CrashInfo::EnumerateAndSuspendThreads() | 
| 102 | { | 
| 103 |     char taskPath[128]; | 
| 104 |     snprintf(taskPath, sizeof(taskPath), "/proc/%d/task" , m_pid); | 
| 105 |  | 
| 106 |     DIR* taskDir = opendir(taskPath); | 
| 107 |     if (taskDir == nullptr) | 
| 108 |     { | 
| 109 |         fprintf(stderr, "opendir(%s) FAILED %s\n" , taskPath, strerror(errno)); | 
| 110 |         return false; | 
| 111 |     } | 
| 112 |  | 
| 113 |     struct dirent* entry; | 
| 114 |     while ((entry = readdir(taskDir)) != nullptr) | 
| 115 |     { | 
| 116 |         pid_t tid = static_cast<pid_t>(strtol(entry->d_name, nullptr, 10)); | 
| 117 |         if (tid != 0) | 
| 118 |         { | 
| 119 |             // Don't suspend the threads if running under sos | 
| 120 |             if (!m_sos) | 
| 121 |             { | 
| 122 |                 //  Reference: http://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads | 
| 123 |                 if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) != -1) | 
| 124 |                 { | 
| 125 |                     int waitStatus; | 
| 126 |                     waitpid(tid, &waitStatus, __WALL); | 
| 127 |                 } | 
| 128 |                 else | 
| 129 |                 { | 
| 130 |                     fprintf(stderr, "ptrace(ATTACH, %d) FAILED %s\n" , tid, strerror(errno)); | 
| 131 |                     closedir(taskDir); | 
| 132 |                     return false; | 
| 133 |                 } | 
| 134 |             } | 
| 135 |             // Add to the list of threads | 
| 136 |             ThreadInfo* thread = new ThreadInfo(tid); | 
| 137 |             m_threads.push_back(thread); | 
| 138 |         } | 
| 139 |     } | 
| 140 |  | 
| 141 |     closedir(taskDir); | 
| 142 |     return true; | 
| 143 | } | 
| 144 |  | 
| 145 | // | 
| 146 | // Gather all the necessary crash dump info. | 
| 147 | // | 
| 148 | bool | 
| 149 | CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) | 
| 150 | { | 
| 151 |     // Get the process info | 
| 152 |     if (!GetStatus(m_pid, &m_ppid, &m_tgid, &m_name)) | 
| 153 |     { | 
| 154 |         return false; | 
| 155 |     } | 
| 156 |     // Get the info about the threads (registers, etc.) | 
| 157 |     for (ThreadInfo* thread : m_threads) | 
| 158 |     { | 
| 159 |         if (!thread->Initialize(m_sos ? m_dataTarget : nullptr)) | 
| 160 |         { | 
| 161 |             return false; | 
| 162 |         } | 
| 163 |     } | 
| 164 |     // Get the auxv data | 
| 165 |     if (!GetAuxvEntries()) | 
| 166 |     { | 
| 167 |         return false; | 
| 168 |     } | 
| 169 |     // Gather all the module memory mappings (from /dev/$pid/maps) | 
| 170 |     if (!EnumerateModuleMappings()) | 
| 171 |     { | 
| 172 |         return false; | 
| 173 |     } | 
| 174 |     // Get shared module debug info | 
| 175 |     if (!GetDSOInfo()) | 
| 176 |     { | 
| 177 |         return false; | 
| 178 |     } | 
| 179 |   | 
| 180 |     for (const MemoryRegion& region : m_moduleAddresses) | 
| 181 |     { | 
| 182 |         region.Trace(); | 
| 183 |     } | 
| 184 |  | 
| 185 |     // If full memory dump, include everything regardless of permissions | 
| 186 |     if (minidumpType & MiniDumpWithFullMemory) | 
| 187 |     { | 
| 188 |         for (const MemoryRegion& region : m_moduleMappings) | 
| 189 |         { | 
| 190 |             InsertMemoryBackedRegion(region); | 
| 191 |         } | 
| 192 |         for (const MemoryRegion& region : m_otherMappings) | 
| 193 |         { | 
| 194 |             InsertMemoryBackedRegion(region); | 
| 195 |         } | 
| 196 |     } | 
| 197 |     // Add all the heap (read/write) memory regions (m_otherMappings contains the heaps) | 
| 198 |     else if (minidumpType & MiniDumpWithPrivateReadWriteMemory) | 
| 199 |     { | 
| 200 |         for (const MemoryRegion& region : m_moduleMappings) | 
| 201 |         { | 
| 202 |             InsertMemoryBackedRegion(region); | 
| 203 |         } | 
| 204 |         for (const MemoryRegion& region : m_otherMappings) | 
| 205 |         { | 
| 206 |             if (region.Permissions() == (PF_R | PF_W)) | 
| 207 |             { | 
| 208 |                 InsertMemoryBackedRegion(region); | 
| 209 |             } | 
| 210 |         } | 
| 211 |     } | 
| 212 |     // Gather all the useful memory regions from the DAC | 
| 213 |     if (!EnumerateMemoryRegionsWithDAC(minidumpType)) | 
| 214 |     { | 
| 215 |         return false; | 
| 216 |     } | 
| 217 |     if ((minidumpType & MiniDumpWithFullMemory) == 0) | 
| 218 |     { | 
| 219 |         // Add the thread's stack and some code memory to core | 
| 220 |         for (ThreadInfo* thread : m_threads) | 
| 221 |         { | 
| 222 |             // Add the thread's stack | 
| 223 |             thread->GetThreadStack(*this); | 
| 224 |         } | 
| 225 |         // All the regions added so far has been backed by memory. Now add the rest of  | 
| 226 |         // mappings so the debuggers like lldb see that an address is code (PF_X) even | 
| 227 |         // if it isn't actually in the core dump. | 
| 228 |         for (const MemoryRegion& region : m_moduleMappings) | 
| 229 |         { | 
| 230 |             assert(!region.IsBackedByMemory()); | 
| 231 |             InsertMemoryRegion(region); | 
| 232 |         } | 
| 233 |         for (const MemoryRegion& region : m_otherMappings) | 
| 234 |         { | 
| 235 |             assert(!region.IsBackedByMemory()); | 
| 236 |             InsertMemoryRegion(region); | 
| 237 |         } | 
| 238 |     } | 
| 239 |     // Join all adjacent memory regions | 
| 240 |     CombineMemoryRegions(); | 
| 241 |     return true; | 
| 242 | } | 
| 243 |  | 
| 244 | void | 
| 245 | CrashInfo::ResumeThreads() | 
| 246 | { | 
| 247 |     if (!m_sos) | 
| 248 |     { | 
| 249 |         for (ThreadInfo* thread : m_threads) | 
| 250 |         { | 
| 251 |             thread->ResumeThread(); | 
| 252 |         } | 
| 253 |     } | 
| 254 | } | 
| 255 |  | 
| 256 | // | 
| 257 | // Get the auxv entries to use and add to the core dump | 
| 258 | // | 
| 259 | bool  | 
| 260 | CrashInfo::GetAuxvEntries() | 
| 261 | { | 
| 262 |     char auxvPath[128]; | 
| 263 |     snprintf(auxvPath, sizeof(auxvPath), "/proc/%d/auxv" , m_pid); | 
| 264 |  | 
| 265 |     int fd = open(auxvPath, O_RDONLY, 0); | 
| 266 |     if (fd == -1) | 
| 267 |     { | 
| 268 |         fprintf(stderr, "open(%s) FAILED %s\n" , auxvPath, strerror(errno)); | 
| 269 |         return false; | 
| 270 |     } | 
| 271 |     bool result = false; | 
| 272 |     elf_aux_entry auxvEntry; | 
| 273 |  | 
| 274 |     while (read(fd, &auxvEntry, sizeof(elf_aux_entry)) == sizeof(elf_aux_entry))  | 
| 275 |     { | 
| 276 |         m_auxvEntries.push_back(auxvEntry); | 
| 277 |         if (auxvEntry.a_type == AT_NULL)  | 
| 278 |         { | 
| 279 |             break; | 
| 280 |         } | 
| 281 |         if (auxvEntry.a_type < AT_MAX)  | 
| 282 |         { | 
| 283 |             m_auxvValues[auxvEntry.a_type] = auxvEntry.a_un.a_val; | 
| 284 |             TRACE("AUXV: %"  PRIu " = %"  PRIxA "\n" , auxvEntry.a_type, auxvEntry.a_un.a_val); | 
| 285 |             result = true; | 
| 286 |         } | 
| 287 |     } | 
| 288 |  | 
| 289 |     close(fd); | 
| 290 |     return result; | 
| 291 | } | 
| 292 |  | 
| 293 | // | 
| 294 | // Get the module mappings for the core dump NT_FILE notes | 
| 295 | // | 
| 296 | bool | 
| 297 | CrashInfo::EnumerateModuleMappings() | 
| 298 | { | 
| 299 |     // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says  | 
| 300 |     // about a library we are looking for. This file looks something like this: | 
| 301 |     // | 
| 302 |     // [address]          [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file | 
| 303 |     // | 
| 304 |     // 35b1800000-35b1820000 r-xp 00000000 08:02 135522  /usr/lib64/ld-2.15.so | 
| 305 |     // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522  /usr/lib64/ld-2.15.so | 
| 306 |     // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522  /usr/lib64/ld-2.15.so | 
| 307 |     // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0       [heap] | 
| 308 |     // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870  /usr/lib64/libc-2.15.so | 
| 309 |     // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so | 
| 310 |     // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so | 
| 311 |     // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870  /usr/lib64/libc-2.15.so | 
| 312 |     char* line = nullptr; | 
| 313 |     size_t lineLen = 0; | 
| 314 |     int count = 0; | 
| 315 |     ssize_t read; | 
| 316 |  | 
| 317 |     // Making something like: /proc/123/maps | 
| 318 |     char mapPath[128]; | 
| 319 |     int chars = snprintf(mapPath, sizeof(mapPath), "/proc/%d/maps" , m_pid); | 
| 320 |     assert(chars > 0 && chars <= sizeof(mapPath)); | 
| 321 |  | 
| 322 |     FILE* mapsFile = fopen(mapPath, "r" ); | 
| 323 |     if (mapsFile == nullptr) | 
| 324 |     { | 
| 325 |         fprintf(stderr, "fopen(%s) FAILED %s\n" , mapPath, strerror(errno)); | 
| 326 |         return false; | 
| 327 |     } | 
| 328 |     // linuxGateAddress is the beginning of the kernel's mapping of | 
| 329 |     // linux-gate.so in the process.  It doesn't actually show up in the | 
| 330 |     // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR | 
| 331 |     // aux vector entry, which gives the information necessary to special | 
| 332 |     // case its entry when creating the list of mappings. | 
| 333 |     // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more | 
| 334 |     // information. | 
| 335 |     const void* linuxGateAddress = (const void*)m_auxvValues[AT_SYSINFO_EHDR]; | 
| 336 |  | 
| 337 |     // Reading maps file line by line  | 
| 338 |     while ((read = getline(&line, &lineLen, mapsFile)) != -1) | 
| 339 |     { | 
| 340 |         uint64_t start, end, offset; | 
| 341 |         char* permissions = nullptr; | 
| 342 |         char* moduleName = nullptr; | 
| 343 |  | 
| 344 |         int c = sscanf(line, "%"  PRIx64 "-%"  PRIx64 " %m[-rwxsp] %"  PRIx64 " %*[:0-9a-f] %*d %ms\n" , &start, &end, &permissions, &offset, &moduleName); | 
| 345 |         if (c == 4 || c == 5) | 
| 346 |         { | 
| 347 |             // r = read | 
| 348 |             // w = write | 
| 349 |             // x = execute | 
| 350 |             // s = shared | 
| 351 |             // p = private (copy on write) | 
| 352 |             uint32_t regionFlags = 0; | 
| 353 |             if (strchr(permissions, 'r')) { | 
| 354 |                 regionFlags |= PF_R; | 
| 355 |             } | 
| 356 |             if (strchr(permissions, 'w')) { | 
| 357 |                 regionFlags |= PF_W; | 
| 358 |             } | 
| 359 |             if (strchr(permissions, 'x')) { | 
| 360 |                 regionFlags |= PF_X; | 
| 361 |             } | 
| 362 |             if (strchr(permissions, 's')) { | 
| 363 |                 regionFlags |= MEMORY_REGION_FLAG_SHARED; | 
| 364 |             } | 
| 365 |             if (strchr(permissions, 'p')) { | 
| 366 |                 regionFlags |= MEMORY_REGION_FLAG_PRIVATE; | 
| 367 |             } | 
| 368 |             MemoryRegion memoryRegion(regionFlags, start, end, offset, moduleName); | 
| 369 |  | 
| 370 |             if (moduleName != nullptr && *moduleName == '/') | 
| 371 |             { | 
| 372 |                 if (m_coreclrPath.empty()) | 
| 373 |                 { | 
| 374 |                     std::string coreclrPath; | 
| 375 |                     coreclrPath.append(moduleName); | 
| 376 |                     size_t last = coreclrPath.rfind(MAKEDLLNAME_A("coreclr" )); | 
| 377 |                     if (last != -1) { | 
| 378 |                         m_coreclrPath = coreclrPath.substr(0, last); | 
| 379 |                     } | 
| 380 |                 } | 
| 381 |                 m_moduleMappings.insert(memoryRegion); | 
| 382 |             } | 
| 383 |             else  | 
| 384 |             { | 
| 385 |                 m_otherMappings.insert(memoryRegion); | 
| 386 |             } | 
| 387 |             if (linuxGateAddress != nullptr && reinterpret_cast<void*>(start) == linuxGateAddress) | 
| 388 |             { | 
| 389 |                 InsertMemoryBackedRegion(memoryRegion); | 
| 390 |             } | 
| 391 |             free(permissions); | 
| 392 |         } | 
| 393 |     } | 
| 394 |  | 
| 395 |     if (g_diagnostics) | 
| 396 |     { | 
| 397 |         TRACE("Module mappings:\n" ); | 
| 398 |         for (const MemoryRegion& region : m_moduleMappings) | 
| 399 |         { | 
| 400 |             region.Trace(); | 
| 401 |         } | 
| 402 |         TRACE("Other mappings:\n" ); | 
| 403 |         for (const MemoryRegion& region : m_otherMappings) | 
| 404 |         { | 
| 405 |             region.Trace(); | 
| 406 |         } | 
| 407 |     } | 
| 408 |  | 
| 409 |     free(line); // We didn't allocate line, but as per contract of getline we should free it | 
| 410 |     fclose(mapsFile); | 
| 411 |  | 
| 412 |     return true; | 
| 413 | } | 
| 414 |  | 
| 415 | // | 
| 416 | // All the shared (native) module info to the core dump | 
| 417 | // | 
| 418 | bool | 
| 419 | CrashInfo::GetDSOInfo() | 
| 420 | { | 
| 421 |     Phdr* phdrAddr = reinterpret_cast<Phdr*>(m_auxvValues[AT_PHDR]); | 
| 422 |     int phnum = m_auxvValues[AT_PHNUM]; | 
| 423 |     assert(m_auxvValues[AT_PHENT] == sizeof(Phdr)); | 
| 424 |     assert(phnum != PN_XNUM); | 
| 425 |  | 
| 426 |     if (phnum <= 0 || phdrAddr == nullptr) { | 
| 427 |         return false; | 
| 428 |     } | 
| 429 |     uint64_t baseAddress = (uint64_t)phdrAddr - sizeof(Ehdr); | 
| 430 |     ElfW(Dyn)* dynamicAddr = nullptr; | 
| 431 |  | 
| 432 |     TRACE("DSO: base %"  PRIA PRIx64 " phdr %p phnum %d\n" , baseAddress, phdrAddr, phnum); | 
| 433 |  | 
| 434 |     // Enumerate program headers searching for the PT_DYNAMIC header, etc. | 
| 435 |     if (!EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, &dynamicAddr)) | 
| 436 |     { | 
| 437 |         return false; | 
| 438 |     } | 
| 439 |     if (dynamicAddr == nullptr) { | 
| 440 |         return false; | 
| 441 |     } | 
| 442 |  | 
| 443 |     // Search for dynamic debug (DT_DEBUG) entry | 
| 444 |     struct r_debug* rdebugAddr = nullptr; | 
| 445 |     for (;;) { | 
| 446 |         ElfW(Dyn) dyn; | 
| 447 |         if (!ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) { | 
| 448 |             fprintf(stderr, "ReadMemory(%p, %"  PRIx ") dyn FAILED\n" , dynamicAddr, sizeof(dyn)); | 
| 449 |             return false; | 
| 450 |         } | 
| 451 |         TRACE("DSO: dyn %p tag %"  PRId " (%"  PRIx ") d_ptr %"  PRIxA "\n" , dynamicAddr, dyn.d_tag, dyn.d_tag, dyn.d_un.d_ptr); | 
| 452 |         if (dyn.d_tag == DT_DEBUG) { | 
| 453 |             rdebugAddr = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr); | 
| 454 |         } | 
| 455 |         else if (dyn.d_tag == DT_NULL) { | 
| 456 |             break; | 
| 457 |         } | 
| 458 |         dynamicAddr++; | 
| 459 |     } | 
| 460 |  | 
| 461 |     // Add the DSO r_debug entry | 
| 462 |     TRACE("DSO: rdebugAddr %p\n" , rdebugAddr); | 
| 463 |     struct r_debug debugEntry; | 
| 464 |     if (!ReadMemory(rdebugAddr, &debugEntry, sizeof(debugEntry))) { | 
| 465 |         fprintf(stderr, "ReadMemory(%p, %"  PRIx ") r_debug FAILED\n" , rdebugAddr, sizeof(debugEntry)); | 
| 466 |         return false; | 
| 467 |     } | 
| 468 |  | 
| 469 |     // Add the DSO link_map entries | 
| 470 |     ArrayHolder<char> moduleName = new char[PATH_MAX]; | 
| 471 |     for (struct link_map* linkMapAddr = debugEntry.r_map; linkMapAddr != nullptr;) { | 
| 472 |         struct link_map map; | 
| 473 |         if (!ReadMemory(linkMapAddr, &map, sizeof(map))) { | 
| 474 |             fprintf(stderr, "ReadMemory(%p, %"  PRIx ") link_map FAILED\n" , linkMapAddr, sizeof(map)); | 
| 475 |             return false; | 
| 476 |         } | 
| 477 |         // Read the module's name and make sure the memory is added to the core dump | 
| 478 |         int i = 0; | 
| 479 |         if (map.l_name != nullptr) { | 
| 480 |             for (; i < PATH_MAX; i++) | 
| 481 |             { | 
| 482 |                 if (!ReadMemory(map.l_name + i, &moduleName[i], 1)) { | 
| 483 |                     TRACE("DSO: ReadMemory link_map name %p + %d FAILED\n" , map.l_name, i); | 
| 484 |                     break; | 
| 485 |                 } | 
| 486 |                 if (moduleName[i] == '\0') { | 
| 487 |                     break; | 
| 488 |                 } | 
| 489 |             } | 
| 490 |         } | 
| 491 |         moduleName[i] = '\0'; | 
| 492 |         TRACE("\nDSO: link_map entry %p l_ld %p l_addr (Ehdr) %"  PRIx " %s\n" , linkMapAddr, map.l_ld, map.l_addr, (char*)moduleName); | 
| 493 |  | 
| 494 |         // Read the ELF header and info adding it to the core dump | 
| 495 |         if (!GetELFInfo(map.l_addr)) { | 
| 496 |             return false; | 
| 497 |         } | 
| 498 |         linkMapAddr = map.l_next; | 
| 499 |     } | 
| 500 |  | 
| 501 |     return true; | 
| 502 | } | 
| 503 |  | 
| 504 | // | 
| 505 | // Add all the necessary ELF headers to the core dump | 
| 506 | // | 
| 507 | bool | 
| 508 | CrashInfo::GetELFInfo(uint64_t baseAddress) | 
| 509 | { | 
| 510 |     if (baseAddress == 0 || baseAddress == m_auxvValues[AT_SYSINFO_EHDR] || baseAddress == m_auxvValues[AT_BASE]) { | 
| 511 |         return true; | 
| 512 |     } | 
| 513 |     Ehdr ehdr; | 
| 514 |     if (!ReadMemory((void*)baseAddress, &ehdr, sizeof(ehdr))) { | 
| 515 |         TRACE("ReadMemory(%p, %"  PRIx ") ehdr FAILED\n" , (void*)baseAddress, sizeof(ehdr)); | 
| 516 |         return true; | 
| 517 |     } | 
| 518 |     int phnum = ehdr.e_phnum; | 
| 519 |     assert(phnum != PN_XNUM); | 
| 520 |     assert(ehdr.e_phentsize == sizeof(Phdr)); | 
| 521 | #ifdef BIT64 | 
| 522 |     assert(ehdr.e_ident[EI_CLASS] == ELFCLASS64); | 
| 523 | #else | 
| 524 |     assert(ehdr.e_ident[EI_CLASS] == ELFCLASS32); | 
| 525 | #endif | 
| 526 |     assert(ehdr.e_ident[EI_DATA] == ELFDATA2LSB); | 
| 527 |  | 
| 528 |     TRACE("ELF: type %d mach 0x%x ver %d flags 0x%x phnum %d phoff %"  PRIxA " phentsize 0x%02x shnum %d shoff %"  PRIxA " shentsize 0x%02x shstrndx %d\n" , | 
| 529 |         ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_flags, phnum, ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_shnum, ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shstrndx); | 
| 530 |  | 
| 531 |     if (ehdr.e_phoff != 0 && phnum > 0) | 
| 532 |     { | 
| 533 |         Phdr* phdrAddr = reinterpret_cast<Phdr*>(baseAddress + ehdr.e_phoff); | 
| 534 |  | 
| 535 |         if (!EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, nullptr)) | 
| 536 |         { | 
| 537 |             return false; | 
| 538 |         } | 
| 539 |     } | 
| 540 |  | 
| 541 |     return true; | 
| 542 | } | 
| 543 |  | 
| 544 | // | 
| 545 | // Enumerate the program headers adding the build id note, unwind frame  | 
| 546 | // region and module addresses to the crash info. | 
| 547 | // | 
| 548 | bool | 
| 549 | CrashInfo::(Phdr* phdrAddr, int phnum, uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr) | 
| 550 | { | 
| 551 |     uint64_t loadbias = baseAddress; | 
| 552 |  | 
| 553 |     for (int i = 0; i < phnum; i++) | 
| 554 |     { | 
| 555 |         Phdr ph; | 
| 556 |         if (!ReadMemory(phdrAddr + i, &ph, sizeof(ph))) { | 
| 557 |             fprintf(stderr, "ReadMemory(%p, %"  PRIx ") phdr FAILED\n" , phdrAddr + i, sizeof(ph)); | 
| 558 |             return false; | 
| 559 |         } | 
| 560 |         if (ph.p_type == PT_LOAD && ph.p_offset == 0) | 
| 561 |         { | 
| 562 |             loadbias -= ph.p_vaddr; | 
| 563 |             TRACE("PHDR: loadbias %"  PRIA PRIx64 "\n" , loadbias); | 
| 564 |             break; | 
| 565 |         } | 
| 566 |     } | 
| 567 |  | 
| 568 |     for (int i = 0; i < phnum; i++) | 
| 569 |     { | 
| 570 |         Phdr ph; | 
| 571 |         if (!ReadMemory(phdrAddr + i, &ph, sizeof(ph))) { | 
| 572 |             fprintf(stderr, "ReadMemory(%p, %"  PRIx ") phdr FAILED\n" , phdrAddr + i, sizeof(ph)); | 
| 573 |             return false; | 
| 574 |         } | 
| 575 |         TRACE("PHDR: %p type %d (%x) vaddr %"  PRIxA " memsz %"  PRIxA " paddr %"  PRIxA " filesz %"  PRIxA " offset %"  PRIxA " align %"  PRIxA "\n" , | 
| 576 |             phdrAddr + i, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align); | 
| 577 |  | 
| 578 |         switch (ph.p_type) | 
| 579 |         { | 
| 580 |         case PT_DYNAMIC: | 
| 581 |             if (pdynamicAddr != nullptr) | 
| 582 |             { | 
| 583 |                 *pdynamicAddr = reinterpret_cast<ElfW(Dyn)*>(loadbias + ph.p_vaddr); | 
| 584 |                 break; | 
| 585 |             } | 
| 586 |             // fall into InsertMemoryRegion | 
| 587 |  | 
| 588 |         case PT_NOTE: | 
| 589 |         case PT_GNU_EH_FRAME: | 
| 590 |             if (ph.p_vaddr != 0 && ph.p_memsz != 0) { | 
| 591 |                 InsertMemoryRegion(loadbias + ph.p_vaddr, ph.p_memsz); | 
| 592 |             } | 
| 593 |             break; | 
| 594 |  | 
| 595 |         case PT_LOAD: | 
| 596 |             MemoryRegion region(0, loadbias + ph.p_vaddr, loadbias + ph.p_vaddr + ph.p_memsz, baseAddress); | 
| 597 |             m_moduleAddresses.insert(region); | 
| 598 |             break; | 
| 599 |         } | 
| 600 |     } | 
| 601 |  | 
| 602 |     return true; | 
| 603 | } | 
| 604 |  | 
| 605 | // | 
| 606 | // Enumerate all the memory regions using the DAC memory region support given a minidump type | 
| 607 | // | 
| 608 | bool | 
| 609 | CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) | 
| 610 | { | 
| 611 |     PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr; | 
| 612 |     ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr; | 
| 613 |     IXCLRDataProcess* pClrDataProcess = nullptr; | 
| 614 |     HMODULE hdac = nullptr; | 
| 615 |     HRESULT hr = S_OK; | 
| 616 |     bool result = false; | 
| 617 |  | 
| 618 |     if (!m_coreclrPath.empty()) | 
| 619 |     { | 
| 620 |         // We assume that the DAC is in the same location as the libcoreclr.so module | 
| 621 |         std::string dacPath; | 
| 622 |         dacPath.append(m_coreclrPath); | 
| 623 |         dacPath.append(MAKEDLLNAME_A("mscordaccore" )); | 
| 624 |  | 
| 625 |         // Load and initialize the DAC | 
| 626 |         hdac = LoadLibraryA(dacPath.c_str()); | 
| 627 |         if (hdac == nullptr) | 
| 628 |         { | 
| 629 |             fprintf(stderr, "LoadLibraryA(%s) FAILED %d\n" , dacPath.c_str(), GetLastError()); | 
| 630 |             goto exit; | 
| 631 |         } | 
| 632 |         pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance" ); | 
| 633 |         if (pfnCLRDataCreateInstance == nullptr) | 
| 634 |         { | 
| 635 |             fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n" , GetLastError()); | 
| 636 |             goto exit; | 
| 637 |         } | 
| 638 |         if ((minidumpType & MiniDumpWithFullMemory) == 0) | 
| 639 |         { | 
| 640 |             hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&pClrDataEnumRegions); | 
| 641 |             if (FAILED(hr)) | 
| 642 |             { | 
| 643 |                 fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n" , hr); | 
| 644 |                 goto exit; | 
| 645 |             } | 
| 646 |             // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC | 
| 647 |             hr = pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); | 
| 648 |             if (FAILED(hr)) | 
| 649 |             { | 
| 650 |                 fprintf(stderr, "EnumMemoryRegions FAILED %08x\n" , hr); | 
| 651 |                 goto exit; | 
| 652 |             } | 
| 653 |         } | 
| 654 |         hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), m_dataTarget, (void**)&pClrDataProcess); | 
| 655 |         if (FAILED(hr)) | 
| 656 |         { | 
| 657 |             fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n" , hr); | 
| 658 |             goto exit; | 
| 659 |         } | 
| 660 |         if (!EnumerateManagedModules(pClrDataProcess)) | 
| 661 |         { | 
| 662 |             goto exit; | 
| 663 |         } | 
| 664 |     } | 
| 665 |     else { | 
| 666 |         TRACE("EnumerateMemoryRegionsWithDAC: coreclr not found; not using DAC\n" ); | 
| 667 |     } | 
| 668 |     if (!UnwindAllThreads(pClrDataProcess)) | 
| 669 |     { | 
| 670 |         goto exit; | 
| 671 |     } | 
| 672 |     result = true; | 
| 673 | exit: | 
| 674 |     if (pClrDataEnumRegions != nullptr) | 
| 675 |     { | 
| 676 |         pClrDataEnumRegions->Release(); | 
| 677 |     } | 
| 678 |     if (pClrDataProcess != nullptr) | 
| 679 |     { | 
| 680 |         pClrDataProcess->Release(); | 
| 681 |     } | 
| 682 |     if (hdac != nullptr) | 
| 683 |     { | 
| 684 |         FreeLibrary(hdac); | 
| 685 |     } | 
| 686 |     return result; | 
| 687 | } | 
| 688 |  | 
| 689 | // | 
| 690 | // Enumerate all the managed modules and replace the module mapping with the module name found. | 
| 691 | // | 
| 692 | bool | 
| 693 | CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess) | 
| 694 | { | 
| 695 |     CLRDATA_ENUM enumModules = 0; | 
| 696 |     bool result = true; | 
| 697 |     HRESULT hr = S_OK; | 
| 698 |  | 
| 699 |     if (FAILED(hr = pClrDataProcess->StartEnumModules(&enumModules))) { | 
| 700 |         fprintf(stderr, "StartEnumModules FAILED %08x\n" , hr); | 
| 701 |         return false; | 
| 702 |     } | 
| 703 |  | 
| 704 |     while (true) | 
| 705 |     { | 
| 706 |         ReleaseHolder<IXCLRDataModule> pClrDataModule; | 
| 707 |         if ((hr = pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) { | 
| 708 |             break; | 
| 709 |         } | 
| 710 |  | 
| 711 |         // Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules. | 
| 712 |         ULONG32 flags; | 
| 713 |         if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) { | 
| 714 |             TRACE("MODULE: GetFlags FAILED %08x\n" , hr); | 
| 715 |             continue; | 
| 716 |         } | 
| 717 |         if (flags & CLRDATA_MODULE_IS_DYNAMIC) { | 
| 718 |             TRACE("MODULE: Skipping dynamic module\n" ); | 
| 719 |             continue; | 
| 720 |         } | 
| 721 |  | 
| 722 |         DacpGetModuleData moduleData; | 
| 723 |         if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr()))) | 
| 724 |         { | 
| 725 |             TRACE("MODULE: %"  PRIA PRIx64 " dyn %d inmem %d file %d pe %"  PRIA PRIx64 " pdb %"  PRIA PRIx64, moduleData.LoadedPEAddress, moduleData.IsDynamic,  | 
| 726 |                 moduleData.IsInMemory, moduleData.IsFileLayout, moduleData.PEFile, moduleData.InMemoryPdbAddress); | 
| 727 |  | 
| 728 |             if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) | 
| 729 |             { | 
| 730 |                 ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; | 
| 731 |                 if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName))) | 
| 732 |                 { | 
| 733 |                     // If the module file name isn't empty | 
| 734 |                     if (wszUnicodeName[0] != 0) { | 
| 735 |                         char* pszName = (char*)malloc(MAX_LONGPATH + 1); | 
| 736 |                         if (pszName == nullptr) { | 
| 737 |                             fprintf(stderr, "Allocating module name FAILED\n" ); | 
| 738 |                             result = false; | 
| 739 |                             break; | 
| 740 |                         } | 
| 741 |                         sprintf_s(pszName, MAX_LONGPATH, "%S" , (WCHAR*)wszUnicodeName); | 
| 742 |                         TRACE(" %s\n" , pszName); | 
| 743 |  | 
| 744 |                         // Change the module mapping name | 
| 745 |                         ReplaceModuleMapping(moduleData.LoadedPEAddress, pszName); | 
| 746 |                     } | 
| 747 |                 } | 
| 748 |                 else { | 
| 749 |                     TRACE("\nModule.GetFileName FAILED %08x\n" , hr); | 
| 750 |                 } | 
| 751 |             } | 
| 752 |             else { | 
| 753 |                 TRACE("\n" ); | 
| 754 |             } | 
| 755 |         } | 
| 756 |         else { | 
| 757 |             TRACE("moduleData.Request FAILED %08x\n" , hr); | 
| 758 |         } | 
| 759 |     } | 
| 760 |  | 
| 761 |     if (enumModules != 0) { | 
| 762 |         pClrDataProcess->EndEnumModules(enumModules); | 
| 763 |     } | 
| 764 |  | 
| 765 |     return result; | 
| 766 | } | 
| 767 |  | 
| 768 | // | 
| 769 | // Unwind all the native threads to ensure that the dwarf unwind info is added to the core dump. | 
| 770 | // | 
| 771 | bool | 
| 772 | CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess) | 
| 773 | { | 
| 774 |     // For each native and managed thread | 
| 775 |     for (ThreadInfo* thread : m_threads) | 
| 776 |     { | 
| 777 |         if (!thread->UnwindThread(*this, pClrDataProcess)) { | 
| 778 |             return false; | 
| 779 |         } | 
| 780 |     } | 
| 781 |     return true; | 
| 782 | } | 
| 783 |  | 
| 784 | // | 
| 785 | // Replace an existing module mapping with one with a different name. | 
| 786 | // | 
| 787 | void | 
| 788 | CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName) | 
| 789 | { | 
| 790 |     // Add or change the module mapping for this PE image. The managed assembly images are | 
| 791 |     // already in the module mappings list but in .NET 2.0 they have the name "/dev/zero". | 
| 792 |     MemoryRegion region(PF_R | PF_W | PF_X, (ULONG_PTR)baseAddress, (ULONG_PTR)(baseAddress + PAGE_SIZE), 0, pszName); | 
| 793 |     const auto& found = m_moduleMappings.find(region); | 
| 794 |     if (found == m_moduleMappings.end()) | 
| 795 |     { | 
| 796 |         m_moduleMappings.insert(region); | 
| 797 |  | 
| 798 |         if (g_diagnostics) { | 
| 799 |             TRACE("MODULE: ADD " ); | 
| 800 |             region.Trace(); | 
| 801 |         } | 
| 802 |     } | 
| 803 |     else | 
| 804 |     { | 
| 805 |         // Create the new memory region with the managed assembly name. | 
| 806 |         MemoryRegion newRegion(*found, pszName); | 
| 807 |  | 
| 808 |         // Remove and cleanup the old one | 
| 809 |         m_moduleMappings.erase(found); | 
| 810 |         const_cast<MemoryRegion&>(*found).Cleanup(); | 
| 811 |  | 
| 812 |         // Add the new memory region | 
| 813 |         m_moduleMappings.insert(newRegion); | 
| 814 |  | 
| 815 |         if (g_diagnostics) { | 
| 816 |             TRACE("MODULE: REPLACE " ); | 
| 817 |             newRegion.Trace(); | 
| 818 |         } | 
| 819 |     } | 
| 820 | } | 
| 821 |  | 
| 822 | // | 
| 823 | // Returns the module base address for the IP or 0. | 
| 824 | // | 
| 825 | uint64_t CrashInfo::GetBaseAddress(uint64_t ip) | 
| 826 | { | 
| 827 |     MemoryRegion search(0, ip, ip, 0); | 
| 828 |     const MemoryRegion* found = SearchMemoryRegions(m_moduleAddresses, search); | 
| 829 |     if (found == nullptr) { | 
| 830 |         return 0; | 
| 831 |     } | 
| 832 |     // The memory region Offset() is the base address of the module | 
| 833 |     return found->Offset(); | 
| 834 | } | 
| 835 |  | 
| 836 | // | 
| 837 | // ReadMemory from target and add to memory regions list | 
| 838 | // | 
| 839 | bool | 
| 840 | CrashInfo::ReadMemory(void* address, void* buffer, size_t size) | 
| 841 | { | 
| 842 |     uint32_t read = 0; | 
| 843 |     if (FAILED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), size, &read))) | 
| 844 |     { | 
| 845 |         return false; | 
| 846 |     } | 
| 847 |     InsertMemoryRegion(reinterpret_cast<uint64_t>(address), size); | 
| 848 |     return true; | 
| 849 | } | 
| 850 |  | 
| 851 | // | 
| 852 | // Add this memory chunk to the list of regions to be  | 
| 853 | // written to the core dump. | 
| 854 | // | 
| 855 | void | 
| 856 | CrashInfo::InsertMemoryRegion(uint64_t address, size_t size) | 
| 857 | { | 
| 858 |     assert(size < UINT_MAX); | 
| 859 |  | 
| 860 |     // Round to page boundary | 
| 861 |     uint64_t start = address & PAGE_MASK; | 
| 862 |     assert(start > 0); | 
| 863 |  | 
| 864 |     // Round up to page boundary | 
| 865 |     uint64_t end = ((address + size) + (PAGE_SIZE - 1)) & PAGE_MASK; | 
| 866 |     assert(end > 0); | 
| 867 |  | 
| 868 |     InsertMemoryRegion(MemoryRegion(GetMemoryRegionFlags(start) | MEMORY_REGION_FLAG_MEMORY_BACKED, start, end)); | 
| 869 | } | 
| 870 |  | 
| 871 | // | 
| 872 | // Adds a memory backed flagged copy of the memory region. The file name is not preserved. | 
| 873 | // | 
| 874 | void | 
| 875 | CrashInfo::InsertMemoryBackedRegion(const MemoryRegion& region) | 
| 876 | { | 
| 877 |     InsertMemoryRegion(MemoryRegion(region, region.Flags() | MEMORY_REGION_FLAG_MEMORY_BACKED)); | 
| 878 | } | 
| 879 |  | 
| 880 | // | 
| 881 | // Add a memory region to the list | 
| 882 | // | 
| 883 | void | 
| 884 | CrashInfo::InsertMemoryRegion(const MemoryRegion& region) | 
| 885 | { | 
| 886 |     // First check if the full memory region can be added without conflicts and is fully valid. | 
| 887 |     const auto& found = m_memoryRegions.find(region); | 
| 888 |     if (found == m_memoryRegions.end()) | 
| 889 |     { | 
| 890 |         // If the region is valid, add the full memory region | 
| 891 |         if (ValidRegion(region)) { | 
| 892 |             m_memoryRegions.insert(region); | 
| 893 |             return; | 
| 894 |         } | 
| 895 |     } | 
| 896 |     else | 
| 897 |     { | 
| 898 |         // If the memory region is wholly contained in region found and both have the  | 
| 899 |         // same backed by memory state, we're done. | 
| 900 |         if (found->Contains(region) && (found->IsBackedByMemory() == region.IsBackedByMemory())) { | 
| 901 |             return; | 
| 902 |         } | 
| 903 |     } | 
| 904 |     // Either part of the region was invalid, part of it hasn't been added or the backed | 
| 905 |     // by memory state is different. | 
| 906 |     uint64_t start = region.StartAddress(); | 
| 907 |  | 
| 908 |     // The region overlaps/conflicts with one already in the set so add one page at a  | 
| 909 |     // time to avoid the overlapping pages. | 
| 910 |     uint64_t numberPages = region.Size() / PAGE_SIZE; | 
| 911 |  | 
| 912 |     for (int p = 0; p < numberPages; p++, start += PAGE_SIZE) | 
| 913 |     { | 
| 914 |         MemoryRegion memoryRegionPage(region.Flags(), start, start + PAGE_SIZE); | 
| 915 |  | 
| 916 |         const auto& found = m_memoryRegions.find(memoryRegionPage); | 
| 917 |         if (found == m_memoryRegions.end()) | 
| 918 |         { | 
| 919 |             // All the single pages added here will be combined in CombineMemoryRegions() | 
| 920 |             if (ValidRegion(memoryRegionPage)) { | 
| 921 |                 m_memoryRegions.insert(memoryRegionPage); | 
| 922 |             } | 
| 923 |         } | 
| 924 |         else { | 
| 925 |             assert(found->IsBackedByMemory() || !region.IsBackedByMemory()); | 
| 926 |         } | 
| 927 |     } | 
| 928 | } | 
| 929 |  | 
| 930 | // | 
| 931 | // Get the memory region flags for a start address | 
| 932 | // | 
| 933 | uint32_t  | 
| 934 | CrashInfo::GetMemoryRegionFlags(uint64_t start) | 
| 935 | { | 
| 936 |     MemoryRegion search(0, start, start + PAGE_SIZE); | 
| 937 |     const MemoryRegion* region = SearchMemoryRegions(m_moduleMappings, search); | 
| 938 |     if (region != nullptr) { | 
| 939 |         return region->Flags(); | 
| 940 |     } | 
| 941 |     region = SearchMemoryRegions(m_otherMappings, search); | 
| 942 |     if (region != nullptr) { | 
| 943 |         return region->Flags(); | 
| 944 |     } | 
| 945 |     TRACE("GetMemoryRegionFlags: FAILED\n" ); | 
| 946 |     return PF_R | PF_W | PF_X; | 
| 947 | } | 
| 948 |  | 
| 949 | // | 
| 950 | // Validates a memory region | 
| 951 | // | 
| 952 | bool | 
| 953 | CrashInfo::ValidRegion(const MemoryRegion& region) | 
| 954 | { | 
| 955 |     if (region.IsBackedByMemory()) | 
| 956 |     { | 
| 957 |         uint64_t start = region.StartAddress(); | 
| 958 |  | 
| 959 |         uint64_t numberPages = region.Size() / PAGE_SIZE; | 
| 960 |         for (int p = 0; p < numberPages; p++, start += PAGE_SIZE) | 
| 961 |         { | 
| 962 |             BYTE buffer[1]; | 
| 963 |             uint32_t read; | 
| 964 |  | 
| 965 |             if (FAILED(m_dataTarget->ReadVirtual(start, buffer, 1, &read))) | 
| 966 |             { | 
| 967 |                 return false; | 
| 968 |             } | 
| 969 |         } | 
| 970 |     } | 
| 971 |     return true; | 
| 972 | } | 
| 973 |  | 
| 974 | // | 
| 975 | // Combine any adjacent memory regions into one | 
| 976 | // | 
| 977 | void | 
| 978 | CrashInfo::CombineMemoryRegions() | 
| 979 | { | 
| 980 |     assert(!m_memoryRegions.empty()); | 
| 981 |  | 
| 982 |     std::set<MemoryRegion> memoryRegionsNew; | 
| 983 |  | 
| 984 |     // MEMORY_REGION_FLAG_SHARED and MEMORY_REGION_FLAG_PRIVATE are internal flags that  | 
| 985 |     // don't affect the core dump so ignore them when comparing the flags. | 
| 986 |     uint32_t flags = m_memoryRegions.begin()->Flags() & (MEMORY_REGION_FLAG_MEMORY_BACKED | MEMORY_REGION_FLAG_PERMISSIONS_MASK); | 
| 987 |     uint64_t start = m_memoryRegions.begin()->StartAddress(); | 
| 988 |     uint64_t end = start; | 
| 989 |  | 
| 990 |     for (const MemoryRegion& region : m_memoryRegions) | 
| 991 |     { | 
| 992 |         // To combine a region it needs to be contiguous, same permissions and memory backed flag. | 
| 993 |         if ((end == region.StartAddress()) &&  | 
| 994 |             (flags == (region.Flags() & (MEMORY_REGION_FLAG_MEMORY_BACKED | MEMORY_REGION_FLAG_PERMISSIONS_MASK)))) | 
| 995 |         { | 
| 996 |             end = region.EndAddress(); | 
| 997 |         } | 
| 998 |         else | 
| 999 |         { | 
| 1000 |             MemoryRegion memoryRegion(flags, start, end); | 
| 1001 |             assert(memoryRegionsNew.find(memoryRegion) == memoryRegionsNew.end()); | 
| 1002 |             memoryRegionsNew.insert(memoryRegion); | 
| 1003 |  | 
| 1004 |             flags = region.Flags() & (MEMORY_REGION_FLAG_MEMORY_BACKED | MEMORY_REGION_FLAG_PERMISSIONS_MASK); | 
| 1005 |             start = region.StartAddress(); | 
| 1006 |             end = region.EndAddress(); | 
| 1007 |         } | 
| 1008 |     } | 
| 1009 |  | 
| 1010 |     assert(start != end); | 
| 1011 |     MemoryRegion memoryRegion(flags, start, end); | 
| 1012 |     assert(memoryRegionsNew.find(memoryRegion) == memoryRegionsNew.end()); | 
| 1013 |     memoryRegionsNew.insert(memoryRegion); | 
| 1014 |  | 
| 1015 |     m_memoryRegions = memoryRegionsNew; | 
| 1016 |  | 
| 1017 |     if (g_diagnostics) | 
| 1018 |     { | 
| 1019 |         TRACE("Memory Regions:\n" ); | 
| 1020 |         for (const MemoryRegion& region : m_memoryRegions) | 
| 1021 |         { | 
| 1022 |             region.Trace(); | 
| 1023 |         } | 
| 1024 |     } | 
| 1025 | } | 
| 1026 |  | 
| 1027 | // | 
| 1028 | // Searches for a memory region given an address. | 
| 1029 | // | 
| 1030 | const MemoryRegion*  | 
| 1031 | CrashInfo::SearchMemoryRegions(const std::set<MemoryRegion>& regions, const MemoryRegion& search) | 
| 1032 | { | 
| 1033 |     std::set<MemoryRegion>::iterator found = regions.find(search); | 
| 1034 |     for (; found != regions.end(); found++) | 
| 1035 |     { | 
| 1036 |         if (search.StartAddress() >= found->StartAddress() && search.StartAddress() < found->EndAddress()) | 
| 1037 |         { | 
| 1038 |             return &*found; | 
| 1039 |         } | 
| 1040 |     } | 
| 1041 | 	return nullptr; | 
| 1042 | } | 
| 1043 |  | 
| 1044 | // | 
| 1045 | // Get the process or thread status | 
| 1046 | // | 
| 1047 | bool | 
| 1048 | CrashInfo::GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name) | 
| 1049 | { | 
| 1050 |     char statusPath[128]; | 
| 1051 |     snprintf(statusPath, sizeof(statusPath), "/proc/%d/status" , pid); | 
| 1052 |  | 
| 1053 |     FILE *statusFile = fopen(statusPath, "r" ); | 
| 1054 |     if (statusFile == nullptr) | 
| 1055 |     { | 
| 1056 |         fprintf(stderr, "GetStatus fopen(%s) FAILED\n" , statusPath); | 
| 1057 |         return false; | 
| 1058 |     } | 
| 1059 |  | 
| 1060 |     *ppid = -1; | 
| 1061 |  | 
| 1062 |     char *line = nullptr; | 
| 1063 |     size_t lineLen = 0; | 
| 1064 |     ssize_t read; | 
| 1065 |     while ((read = getline(&line, &lineLen, statusFile)) != -1) | 
| 1066 |     { | 
| 1067 |         if (strncmp("PPid:\t" , line, 6) == 0) | 
| 1068 |         { | 
| 1069 |             *ppid = atoll(line + 6); | 
| 1070 |         } | 
| 1071 |         else if (strncmp("Tgid:\t" , line, 6) == 0) | 
| 1072 |         { | 
| 1073 |             *tgid = atoll(line + 6); | 
| 1074 |         } | 
| 1075 |         else if (strncmp("Name:\t" , line, 6) == 0) | 
| 1076 |         { | 
| 1077 |             if (name != nullptr) | 
| 1078 |             { | 
| 1079 |                 char* n = strchr(line + 6, '\n'); | 
| 1080 |                 if (n != nullptr)  | 
| 1081 |                 { | 
| 1082 |                     *n = '\0'; | 
| 1083 |                 } | 
| 1084 |                 *name = strdup(line + 6); | 
| 1085 |             } | 
| 1086 |         } | 
| 1087 |     } | 
| 1088 |  | 
| 1089 |     free(line); | 
| 1090 |     fclose(statusFile); | 
| 1091 |     return true; | 
| 1092 | } | 
| 1093 |  |