|  | // Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/sampling_heap_profiler/module_cache.h" | 
|  |  | 
|  | #include <dlfcn.h> | 
|  | #include <mach-o/getsect.h> | 
|  | #include <uuid/uuid.h> | 
|  |  | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "starboard/types.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Returns the unique build ID for a module loaded at |module_addr|. Returns the | 
|  | // empty string if the function fails to get the build ID. | 
|  | // | 
|  | // Build IDs are created by the concatenation of the module's GUID (Windows) / | 
|  | // UUID (Mac) and an "age" field that indicates how many times that GUID/UUID | 
|  | // has been reused. In Windows binaries, the "age" field is present in the | 
|  | // module header, but on the Mac, UUIDs are never reused and so the "age" value | 
|  | // appended to the UUID is always 0. | 
|  | std::string GetUniqueId(const void* module_addr) { | 
|  | const mach_header_64* mach_header = | 
|  | reinterpret_cast<const mach_header_64*>(module_addr); | 
|  | DCHECK_EQ(MH_MAGIC_64, mach_header->magic); | 
|  |  | 
|  | size_t offset = sizeof(mach_header_64); | 
|  | size_t offset_limit = sizeof(mach_header_64) + mach_header->sizeofcmds; | 
|  |  | 
|  | for (uint32_t i = 0; i < mach_header->ncmds; ++i) { | 
|  | if (offset + sizeof(load_command) >= offset_limit) | 
|  | return std::string(); | 
|  |  | 
|  | const load_command* current_cmd = reinterpret_cast<const load_command*>( | 
|  | reinterpret_cast<const uint8_t*>(mach_header) + offset); | 
|  |  | 
|  | if (offset + current_cmd->cmdsize > offset_limit) { | 
|  | // This command runs off the end of the command list. This is malformed. | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | if (current_cmd->cmd == LC_UUID) { | 
|  | if (current_cmd->cmdsize < sizeof(uuid_command)) { | 
|  | // This "UUID command" is too small. This is malformed. | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | const uuid_command* uuid_cmd = | 
|  | reinterpret_cast<const uuid_command*>(current_cmd); | 
|  | static_assert(sizeof(uuid_cmd->uuid) == sizeof(uuid_t), | 
|  | "UUID field of UUID command should be 16 bytes."); | 
|  | // The ID is comprised of the UUID concatenated with the Mac's "age" value | 
|  | // which is always 0. | 
|  | return HexEncode(&uuid_cmd->uuid, sizeof(uuid_cmd->uuid)) + "0"; | 
|  | } | 
|  | offset += current_cmd->cmdsize; | 
|  | } | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | ModuleCache::Module ModuleCache::CreateModuleForAddress(uintptr_t address) { | 
|  | Dl_info inf; | 
|  | if (!dladdr(reinterpret_cast<const void*>(address), &inf)) | 
|  | return Module(); | 
|  | auto base_module_address = reinterpret_cast<uintptr_t>(inf.dli_fbase); | 
|  | return Module(base_module_address, GetUniqueId(inf.dli_fbase), | 
|  | FilePath(inf.dli_fname), GetModuleTextSize(inf.dli_fbase)); | 
|  | } | 
|  |  | 
|  | size_t ModuleCache::GetModuleTextSize(const void* module_addr) { | 
|  | const mach_header_64* mach_header = | 
|  | reinterpret_cast<const mach_header_64*>(module_addr); | 
|  | DCHECK_EQ(MH_MAGIC_64, mach_header->magic); | 
|  | unsigned long module_size; | 
|  | getsegmentdata(mach_header, SEG_TEXT, &module_size); | 
|  | return module_size; | 
|  | } | 
|  |  | 
|  | }  // namespace base |