blob: 85e1ab6be4687f7878b3cbea72a07eb8d44b430a [file] [log] [blame]
// Copyright 2020 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 <cstdint>
#include <iostream>
#include <memory>
#include <string_view>
#include <unordered_map>
#include <rapidjson/document.h>
#include "src/developer/debug/shared/message_loop_poll.h"
#include "src/developer/debug/zxdb/client/download_observer.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/system.h"
#include "src/developer/debug/zxdb/client/system_observer.h"
#include "src/developer/debug/zxdb/symbols/module_symbols.h"
#include "tools/symbolizer/analytics.h"
#include "tools/symbolizer/command_line_options.h"
#include "tools/symbolizer/printer.h"
#include "tools/symbolizer/symbolizer.h"
namespace symbolizer {
// This is the core logic of the symbolizer. We provide a MockSymbolizer and a SymbolizerImpl for
// better testing.
class SymbolizerImpl : public Symbolizer,
public zxdb::DownloadObserver,
public zxdb::SystemObserver {
using AnalyticsSender = std::function<void(const analytics::google_analytics::Hit&)>;
SymbolizerImpl(Printer* printer, const CommandLineOptions& options,
AnalyticsSender analytics_sender = nullptr);
~SymbolizerImpl() override;
// |Symbolizer| implementation.
void Reset(bool symbolizing_dart) override;
void Module(uint64_t id, std::string_view name, std::string_view build_id) override;
void MMap(uint64_t address, uint64_t size, uint64_t module_id, std::string_view flags,
uint64_t module_offset) override;
void Backtrace(uint64_t frame_id, uint64_t address, AddressType type,
std::string_view message) override;
void DumpFile(std::string_view type, std::string_view name) override;
// |DownloadObserver| implementation.
void OnDownloadsStarted() override;
void OnDownloadsStopped(size_t num_succeeded, size_t num_failed) override;
// |SystemObserver| implementation.
void DidCreateSymbolServer(zxdb::SymbolServer* server) override;
void OnSymbolServerStatusChanged(zxdb::SymbolServer* server) override;
// Ensures a process is created on target_. Should be called before each Bactrace().
void InitProcess();
// Resets dumpfile_current_object_.
void ResetDumpfileCurrentObject();
// Helper to convert a string_view to a rapidjson string.
rapidjson::Value ToJSONString(std::string_view str);
// Non-owning.
Printer* printer_;
// The main message loop.
debug::MessageLoopPoll loop_;
// The entry for interacting with zxdb.
zxdb::Session session_;
// Owned by session_. Holds the process we're working on.
zxdb::Target* target_;
// Whether there are symbol servers and we're waiting for authentication.
bool waiting_auth_ = false;
// Whether there are symbol downloads in progress.
bool is_downloading_ = false;
struct ModuleInfo {
std::string name;
std::string build_id;
uint64_t base = 0; // Load address of the module.
uint64_t size = 0; // Range of the module.
// Zircon on x64 has a negative base address, i.e. the module offset is larger than the load
// address. Since zxdb doesn't support that, we load the module at 0 and modify the pc for all
// frames.
// At least one of the base and the negative_base must be zero.
uint64_t negative_base = 0;
bool printed = false; // Whether we've printed the module info.
// Mapping from module_id (available in the log) to module info.
// module_id is usually a sequence from 0 used to associate "mmap" commands with "module"
// commands. It's different from build_id.
std::unordered_map<uint64_t, ModuleInfo> modules_;
// Holds symbol data from the previously handled stack trace.
// Replaced immediately once a new stack trace is handled.
std::vector<fxl::RefPtr<zxdb::ModuleSymbols>> previous_modules_;
// Mapping from base address of each module to the module_id.
// Useful when doing binary search for the module from an address.
std::map<uint64_t, uint64_t> address_to_module_id_;
// Whether to omit the [[[ELF module]]] lines.
bool omit_module_lines_ = false;
// The JSON file to write the dumpfile output. If it's empty then nothing will be written and
// dumpfile_array_ and dumpfile_current_object_ will be useless.
std::string dumpfile_output_;
// The JSON document/array that holds the dumpfile output. The content will be written to
// dumpfile_output_ when we destruct.
rapidjson::Document dumpfile_document_;
// Object that will be appended to dumpfile_output_array_ on the next DumpFile(). It'll be like {
// "modules": [
// {
// "name": "",
// "build": "3552581785f71a08",
// "id": 7
// },
// ...
// ],
// "segments": [
// {
// "mod": 0,
// "vaddr": 38628535922688,
// "size": 61440,
// "flags": "r",
// "mod_rel_addr": 0
// },
// ...
// ],
// }.
// Each Module() will be appended to the modules array and each MMap() will be appended to the
// segments array. We're keeping a separate copy of those info because
// 1) not all mmap info is kept in ModuleInfo.
// 2) the dumpfile feature might be removed in the future.
rapidjson::Value dumpfile_current_object_;
// Analytics. Instead of keeping a unique_ptr, we depends on the valid() method to know if
// the analytics is not empty and worth sending.
SymbolizationAnalyticsBuilder analytics_builder_;
bool remote_symbol_lookup_enabled_ = false;
AnalyticsSender sender_;
// Whether we're symbolizing a Dart stack trace.
bool symbolizing_dart_ = false;
} // namespace symbolizer