blob: faf4384d45ad37c09f7ae7f2e7d140d07a0b3beb [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/console/input_location_parser.h"
#include <inttypes.h>
#include "garnet/bin/zxdb/client/frame.h"
#include "garnet/bin/zxdb/client/process.h"
#include "garnet/bin/zxdb/console/command_utils.h"
#include "garnet/bin/zxdb/console/string_util.h"
#include "garnet/bin/zxdb/symbols/location.h"
#include "garnet/bin/zxdb/symbols/process_symbols.h"
#include "lib/fxl/strings/string_printf.h"
namespace zxdb {
Err ParseInputLocation(const Frame* frame, const std::string& input,
InputLocation* location) {
if (input.empty())
return Err("Passed empty location.");
// Check for one colon. Two colons is a C++ member function.
size_t colon = input.find(':');
if (colon != std::string::npos && colon < input.size() - 1 &&
input[colon + 1] != ':') {
// <file>:<line> format.
std::string file = input.substr(0, colon);
uint64_t line = 0;
Err err = StringToUint64(input.substr(colon + 1), &line);
if (err.has_error())
return err;
location->type = InputLocation::Type::kLine;
location->line = FileLine(std::move(file), static_cast<int>(line));
return Err();
}
// Check for memory addresses.
bool is_address = false;
size_t address_begin = 0; // Index of address number when is_address.
if (input[0] == '*') {
// *<address> format
is_address = true;
address_begin = 1; // Skip "*".
} else if (CheckHexPrefix(input)) {
// Hex numbers are addresses.
is_address = true;
address_begin = 0;
}
if (is_address) {
std::string addr_str = input.substr(address_begin);
Err err = StringToUint64(addr_str, &location->address);
if (err.has_error())
return err;
location->type = InputLocation::Type::kAddress;
return Err();
}
uint64_t line = 0;
Err err = StringToUint64(input, &line);
if (err.has_error()) {
// Not a number, assume symbol.
location->type = InputLocation::Type::kSymbol;
location->symbol = input;
return Err();
}
// Just a number, use the file name from the specified frame.
if (!frame) {
return Err(
"There is no current frame to get a file name, you'll have to "
"specify an explicit frame or file name.");
}
const Location& frame_location = frame->GetLocation();
if (frame_location.file_line().file().empty()) {
return Err(
"The current frame doesn't have a file name to use, you'll "
"have to specify a file.");
}
location->type = InputLocation::Type::kLine;
location->line =
FileLine(frame_location.file_line().file(), static_cast<int>(line));
return Err();
}
Err ResolveInputLocations(const ProcessSymbols* process_symbols,
const InputLocation& input_location, bool symbolize,
std::vector<Location>* locations) {
ResolveOptions options;
options.symbolize = symbolize;
*locations = process_symbols->ResolveInputLocation(input_location, options);
if (locations->empty()) {
return Err("Nothing matching this %s was found.",
InputLocation::TypeToString(input_location.type));
}
return Err();
}
Err ResolveInputLocations(const ProcessSymbols* process_symbols,
const Frame* optional_frame, const std::string& input,
bool symbolize, std::vector<Location>* locations) {
InputLocation input_location;
Err err = ParseInputLocation(optional_frame, input, &input_location);
if (err.has_error())
return err;
return ResolveInputLocations(process_symbols, input_location, symbolize,
locations);
}
Err ResolveUniqueInputLocation(const ProcessSymbols* process_symbols,
const InputLocation& input_location,
bool symbolize, Location* location) {
std::vector<Location> locations;
Err err = ResolveInputLocations(process_symbols, input_location, symbolize,
&locations);
if (err.has_error())
return err;
FXL_DCHECK(!locations.empty()); // Non-empty on success should be guaranteed.
if (locations.size() == 1u) {
// Success, got a unique location.
*location = locations[0];
return Err();
}
// When there is more than one, generate an error that lists the
// possibilities for disambiguation.
std::string err_str = "This resolves to more than one location. Could be:\n";
constexpr size_t kMaxSuggestions = 10u;
if (!symbolize) {
// The original call did not request symbolization which will produce very
// non-helpful suggestions. We're not concerned about performance in this
// error case so re-query to get the full symbols.
locations.clear();
ResolveInputLocations(process_symbols, input_location, true, &locations);
}
for (size_t i = 0; i < locations.size() && i < kMaxSuggestions; i++) {
// Always show the full path since we're doing disambiguation and the
// problem could have been two files with the same name but different
// paths.
err_str += fxl::StringPrintf(" %s ", GetBullet().c_str());
if (locations[i].file_line().is_valid()) {
err_str += DescribeFileLine(locations[i].file_line(), true);
err_str += fxl::StringPrintf(" = 0x%" PRIx64, locations[i].address());
} else {
err_str += FormatLocation(locations[i], true, false).AsString();
}
err_str += "\n";
}
if (locations.size() > kMaxSuggestions) {
err_str += fxl::StringPrintf("...%zu more omitted...\n",
locations.size() - kMaxSuggestions);
}
return Err(err_str);
}
Err ResolveUniqueInputLocation(const ProcessSymbols* process_symbols,
const Frame* optional_frame,
const std::string& input, bool symbolize,
Location* location) {
InputLocation input_location;
Err err = ParseInputLocation(optional_frame, input, &input_location);
if (err.has_error())
return err;
return ResolveUniqueInputLocation(process_symbols, input_location, symbolize,
location);
}
} // namespace zxdb