Commit c5b3973d authored by sque's avatar sque Committed by Commit bot

Read Chrome build ID and store it in leak reports

The build ID reading code is based on:
src/native_client/src/untrusted/minidump_generator/build_id.cc

BUG=chromium:627873
TEST=build successfully

Review-Url: https://codereview.chromium.org/2159013002
Cr-Commit-Position: refs/heads/master@{#407619}
parent ad93be59
......@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "components/metrics/leak_detector/gnu_build_id_reader.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
......@@ -90,6 +91,9 @@ MemoryLeakReportProto::Params GetVariationParameters() {
LeakDetectorController::LeakDetectorController()
: params_(GetVariationParameters()) {
// Read the build ID once and store it.
leak_detector::gnu_build_id_reader::ReadBuildID(&build_id_);
LeakDetector* detector = LeakDetector::GetInstance();
detector->AddObserver(this);
......@@ -138,6 +142,8 @@ void LeakDetectorController::StoreLeakReports(
stored_reports_.push_back(report);
stored_reports_.back().mutable_params()->CopyFrom(params_);
stored_reports_.back().set_source_process(process_type);
stored_reports_.back().mutable_build_id()->assign(build_id_.begin(),
build_id_.end());
}
}
......
......@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_METRICS_LEAK_DETECTOR_LEAK_DETECTOR_CONTROLLER_H_
#define CHROME_BROWSER_METRICS_LEAK_DETECTOR_LEAK_DETECTOR_CONTROLLER_H_
#include <stdint.h>
#include <vector>
#include "base/macros.h"
......@@ -53,6 +55,9 @@ class LeakDetectorController
// protobuf message instead of in separate member variables.
const MemoryLeakReportProto::Params params_;
// The build ID of the current Chrome binary.
std::vector<uint8_t> build_id_;
// For thread safety.
base::ThreadChecker thread_checker_;
......
......@@ -270,6 +270,8 @@
'metrics/leak_detector/call_stack_table.h',
'metrics/leak_detector/custom_allocator.cc',
'metrics/leak_detector/custom_allocator.h',
'metrics/leak_detector/gnu_build_id_reader.cc',
'metrics/leak_detector/gnu_build_id_reader.h',
'metrics/leak_detector/leak_analyzer.cc',
'metrics/leak_detector/leak_analyzer.h',
'metrics/leak_detector/leak_detector.cc',
......
......@@ -14,6 +14,8 @@ if (is_chromeos) {
"call_stack_table.h",
"custom_allocator.cc",
"custom_allocator.h",
"gnu_build_id_reader.cc",
"gnu_build_id_reader.h",
"leak_analyzer.cc",
"leak_analyzer.h",
"leak_detector.cc",
......
// Copyright 2016 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 "components/metrics/leak_detector/gnu_build_id_reader.h"
#include <elf.h>
#include <algorithm>
#if defined(OS_CHROMEOS)
#include <link.h> // for dl_iterate_phdr
#else
#error "Getting binary mapping info is not supported on this platform."
#endif // defined(OS_CHROMEOS)
namespace metrics {
namespace leak_detector {
namespace gnu_build_id_reader {
namespace {
// Contains data passed to dl_iterate_phdr() for reading build ID.
struct ReadBuildIDData {
// Points to a vector for storing the build ID.
std::vector<uint8_t>* build_id;
// Indicates whether build ID was read successfully.
bool success;
};
// Given a pointer and an offset, add the offset to the pointer and round it up
// to the next uint32_t.
const void* AlignPtrAndOffsetToUint32(const void* ptr, intptr_t offset) {
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + offset;
uintptr_t aligned_addr =
(addr + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
return reinterpret_cast<const void*>(aligned_addr);
}
// Searches for the ELF note containing the build ID within the data range
// specified by [start, end). Returns the build ID in |*result|. If the build ID
// is not found, |*result| will be unchanged. Returns true if the build ID was
// successfully read or false otherwise.
bool GetBuildIdFromNotes(const void* start,
const void* end,
std::vector<uint8_t>* result) {
using NoteHeaderPtr = const ElfW(Nhdr)*;
NoteHeaderPtr note = reinterpret_cast<NoteHeaderPtr>(start);
while (note < end) {
const char* name_ptr = reinterpret_cast<const char*>(note + 1);
if (name_ptr >= end) {
break;
}
// |desc_ptr| points the to the actual build ID data.
const uint8_t* desc_ptr = reinterpret_cast<const uint8_t*>(
AlignPtrAndOffsetToUint32(name_ptr, note->n_namesz));
if (note->n_type == NT_GNU_BUILD_ID &&
note->n_namesz == sizeof(ELF_NOTE_GNU) &&
std::equal(name_ptr, name_ptr + sizeof(ELF_NOTE_GNU), ELF_NOTE_GNU)) {
result->assign(desc_ptr, desc_ptr + note->n_descsz);
return true;
}
NoteHeaderPtr next_ptr = reinterpret_cast<NoteHeaderPtr>(
AlignPtrAndOffsetToUint32(desc_ptr, note->n_descsz));
note = next_ptr;
}
return false;
}
// Callback for dl_iterate_phdr(). Finds the notes section and looks for the
// build ID in there. |data| points to a ReadBuildIDData struct whose |build_id|
// field should point to a valid std::vector<uint8_t>. Returns the build ID in
// that field, and sets |ReadBuildIDData::success| based on whether the build ID
// was successfully read.
//
// This function always returns 1 to signal the end of dl_iterate_phdr()'s
// iteration, because it is only interested in the first binary iterated by
// dl_iterate_phdr(), which is the current binary.
int FindNotesAndGetBuildID(struct dl_phdr_info* info,
size_t /* size */,
void* data) {
uintptr_t mapping_addr = reinterpret_cast<uintptr_t>(info->dlpi_addr);
const ElfW(Ehdr)* file_header =
reinterpret_cast<const ElfW(Ehdr)*>(mapping_addr);
// Make sure that a valid |mapping_addr| was read.
if (!file_header || file_header->e_phentsize != sizeof(ElfW(Phdr))) {
return 1;
}
// Find the ELF segment header for the NOTES section.
for (int i = 0; i < info->dlpi_phnum; ++i) {
const ElfW(Phdr)& segment_header = info->dlpi_phdr[i];
if (segment_header.p_type == PT_NOTE) {
const void* note = reinterpret_cast<const void*>(
mapping_addr + segment_header.p_vaddr);
const void* note_end = reinterpret_cast<const void*>(
mapping_addr + segment_header.p_vaddr + segment_header.p_memsz);
ReadBuildIDData* read_data = reinterpret_cast<ReadBuildIDData*>(data);
read_data->success =
GetBuildIdFromNotes(note, note_end, read_data->build_id);
}
}
return 1;
}
} // namespace
bool ReadBuildID(std::vector<uint8_t>* build_id) {
ReadBuildIDData data;
data.build_id = build_id;
data.success = false;
#if defined(OS_CHROMEOS)
dl_iterate_phdr(FindNotesAndGetBuildID, &data);
#endif // defined(OS_CHROMEOS)
return data.success;
}
} // namespace gnu_build_id_reader
} // namespace leak_detector
} // namespace metrics
// Copyright 2016 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.
#ifndef COMPONENTS_METRICS_LEAK_DETECTOR_GNU_BUILD_ID_READER_H_
#define COMPONENTS_METRICS_LEAK_DETECTOR_GNU_BUILD_ID_READER_H_
#include <stdint.h>
#include <vector>
namespace metrics {
namespace leak_detector {
namespace gnu_build_id_reader {
// Reads the build ID from the GNU build notes and stores it in |*build_id|.
// Returns true if it was successfully read, or false otherwise.
bool ReadBuildID(std::vector<uint8_t>* build_id);
} // namespace gnu_build_id_reader
} // namespace leak_detector
} // namespace metrics
#endif // COMPONENTS_METRICS_LEAK_DETECTOR_GNU_BUILD_ID_READER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment