object-introspection/oi/Descs.cpp
2023-04-26 16:20:53 +01:00

129 lines
3.3 KiB
C++

/*
* 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 "oi/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;
}