object-introspection/src/Descs.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

129 lines
3.3 KiB
C++
Raw Normal View History

2022-12-19 14:37:51 +00:00
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Descs.h"
#include <glog/logging.h>
#include <algorithm>
#include <boost/scope_exit.hpp>
#include <charconv>
#include <iostream>
#include <utility>
extern "C" {
#include <drgn.h>
#include <sys/user.h>
}
std::ostream &operator<<(std::ostream &os, const FuncDesc::Range &r) {
return os << (void *)r.start << ':' << (void *)r.end;
}
/*
* Given a register set return the address where the supplied argument
* position can be found at the given pc (what about if we don't have this
* location?).
*/
std::optional<uintptr_t> FuncDesc::Arg::findAddress(
struct user_regs_struct *regs, uintptr_t pc) const {
auto prevRip = std::exchange(regs->rip, pc);
BOOST_SCOPE_EXIT_ALL(&) {
regs->rip = prevRip;
};
struct drgn_object object {};
BOOST_SCOPE_EXIT_ALL(&) {
drgn_object_deinit(&object);
};
if (auto *err = drgn_object_locate(&locator, regs, &object)) {
LOG(ERROR) << "Error while finding address of argument: " << err->message;
drgn_error_destroy(err);
return std::nullopt;
}
return object.address;
}
std::optional<uint8_t> FuncDesc::getArgumentIndex(const std::string &arg,
bool validateIndex) const {
if (arg == "retval") {
return std::nullopt;
}
if (arg == "this") {
if (!isMethod) {
LOG(ERROR) << "Function " << symName << " has no 'this' parameter";
return std::nullopt;
}
return 0;
}
//
// Extract arg's number
auto it = arg.find_first_of("0123456789");
if (it == std::string::npos) {
LOG(ERROR) << "Invalid argument: " << arg;
return std::nullopt;
}
const auto *argIdxBegin = arg.data() + it;
const auto *argIdxEnd = arg.data() + arg.size();
uint8_t argIdx = 0;
if (auto res = std::from_chars(argIdxBegin, argIdxEnd, argIdx);
res.ec != std::errc{}) {
LOG(ERROR) << "Failed to convert " << arg
<< " digits: " << strerror((int)res.ec);
return std::nullopt;
}
// Check and offset for methods
if (validateIndex && argIdx >= numArgs()) {
LOG(ERROR) << "Argument index " << (int)argIdx
<< " too large. Args count: " << numArgs();
return std::nullopt;
}
if (isMethod) {
argIdx += 1;
}
return argIdx;
}
std::shared_ptr<FuncDesc::TargetObject> FuncDesc::getArgument(
const std::string &arg) {
std::shared_ptr<FuncDesc::TargetObject> outArg;
if (arg == "retval") {
outArg = retval;
} else {
auto argIdx = getArgumentIndex(arg);
if (!argIdx.has_value()) {
return nullptr;
}
outArg = arguments[*argIdx];
}
if (!outArg || !outArg->valid) {
LOG(ERROR) << "Argument " << arg << " for " << symName << " is invalid";
return nullptr;
}
return outArg;
}