blob: 6d9585a449fb19d00c3e036c9be2488a5d5b7125 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "garnet/bin/zxdb/symbols/build_id_index.h"
#include <filesystem>
#include "garnet/lib/debug_ipc/helper/elf.h"
#include "lib/fxl/strings/string_printf.h"
#include "lib/fxl/strings/string_view.h"
#include "lib/fxl/strings/trim.h"
namespace zxdb {
BuildIDIndex::BuildIDIndex() = default;
BuildIDIndex::~BuildIDIndex() = default;
std::string BuildIDIndex::FileForBuildID(const std::string& build_id) {
EnsureCacheClean();
auto found = build_id_to_file_.find(build_id);
if (found == build_id_to_file_.end())
return std::string();
return found->second;
}
void BuildIDIndex::AddBuildIDMapping(const std::string& build_id,
const std::string& file_name) {
// This map saves the manual mapping across cache updates.
manual_mappings_[build_id] = file_name;
// Don't bother marking the cache dirty since we can just add it.
build_id_to_file_[build_id] = file_name;
}
void BuildIDIndex::AddBuildIDMappingFile(const std::string& id_file_name) {
// If the file is already loaded, ignore it.
if (std::find(build_id_files_.begin(), build_id_files_.end(), id_file_name) !=
build_id_files_.end())
return;
build_id_files_.emplace_back(id_file_name);
ClearCache();
}
void BuildIDIndex::AddSymbolSource(const std::string& path) {
// If the file is already loaded, ignore it.
if (std::find(sources_.begin(), sources_.end(), path) != sources_.end())
return;
sources_.emplace_back(path);
ClearCache();
}
BuildIDIndex::StatusList BuildIDIndex::GetStatus() {
EnsureCacheClean();
return status_;
}
void BuildIDIndex::ClearCache() {
build_id_to_file_.clear();
status_.clear();
cache_dirty_ = true;
}
// static
int BuildIDIndex::ParseIDs(const std::string& input, IDMap* output) {
int added = 0;
for (size_t line_begin = 0; line_begin < input.size(); line_begin++) {
size_t newline = input.find('\n', line_begin);
if (newline == std::string::npos)
newline = input.size();
fxl::StringView line(&input[line_begin], newline - line_begin);
if (!line.empty()) {
// Format is <buildid> <space> <filename>
size_t first_space = line.find(' ');
if (first_space != std::string::npos && first_space > 0 &&
first_space + 1 < line.size()) {
// There is a space and it separates two nonempty things.
fxl::StringView to_trim(" \t\r\n");
fxl::StringView build_id =
fxl::TrimString(line.substr(0, first_space), to_trim);
fxl::StringView path = fxl::TrimString(
line.substr(first_space + 1, line.size() - first_space - 1),
to_trim);
added++;
output->emplace(std::piecewise_construct,
std::forward_as_tuple(build_id.data(), build_id.size()),
std::forward_as_tuple(path.data(), path.size()));
}
}
line_begin = newline; // The for loop will advance past this.
}
return added;
}
void BuildIDIndex::LogMessage(const std::string& msg) const {
if (information_callback_)
information_callback_(msg);
}
void BuildIDIndex::LoadOneBuildIDFile(const std::string& file_name) {
FILE* id_file = fopen(file_name.c_str(), "r");
if (!id_file) {
status_.emplace_back(file_name, 0);
LogMessage("Can't open build ID file: " + file_name);
return;
}
fseek(id_file, 0, SEEK_END);
long length = ftell(id_file);
if (length <= 0) {
status_.emplace_back(file_name, 0);
LogMessage("Can't load build ID file: " + file_name);
return;
}
std::string contents;
contents.resize(length);
fseek(id_file, 0, SEEK_SET);
if (fread(&contents[0], 1, contents.size(), id_file) !=
static_cast<size_t>(length)) {
status_.emplace_back(file_name, 0);
LogMessage("Can't read build ID file: " + file_name);
return;
}
fclose(id_file);
int added = ParseIDs(contents, &build_id_to_file_);
status_.emplace_back(file_name, added);
if (!added)
LogMessage("No mappings found in build ID file: " + file_name);
}
void BuildIDIndex::IndexOneSourcePath(const std::string& path) {
std::error_code ec;
if (std::filesystem::is_directory(path, ec)) {
// Iterate through all files in this directory, but don't recurse.
int indexed = 0;
for (const auto& child : std::filesystem::directory_iterator(path)) {
if (IndexOneSourceFile(child.path()))
indexed++;
}
status_.emplace_back(path, indexed);
} else if (!ec) {
if (IndexOneSourceFile(path)) {
status_.emplace_back(path, 1);
} else {
status_.emplace_back(path, 0);
LogMessage(fxl::StringPrintf("Symbol file could not be loaded: %s",
path.c_str()));
}
}
}
bool BuildIDIndex::IndexOneSourceFile(const std::string& file_path) {
FILE* file = fopen(file_path.c_str(), "rb");
if (!file)
return false;
std::string build_id = debug_ipc::ExtractBuildID(file);
fclose(file);
if (!build_id.empty()) {
build_id_to_file_[build_id] = file_path;
return true;
}
return false;
}
void BuildIDIndex::EnsureCacheClean() {
if (!cache_dirty_)
return;
for (const auto& build_id_file : build_id_files_)
LoadOneBuildIDFile(build_id_file);
for (const auto& source : sources_)
IndexOneSourcePath(source);
for (const auto& mapping : manual_mappings_)
build_id_to_file_.insert(mapping);
cache_dirty_ = false;
}
} // namespace zxdb