pind is Pin's server. Each instrumented process has an accompanying pind server that allows amongst other things to load user written plugins that implement functions that can be called remotely by Pintools. These plugins are implemented in DLLs (shared objects) that are linked against system libraries and so are not limited as Pintools are to use Pin runtime libraries.
#include <cassert>
#include <cstring>
#include <fstream>
#include <utility>
#include <set>
#include <map>
#include <iomanip>
#include <mutex>
#include <iostream>
#include <string>
#if __has_include(<filesystem>)
#include <filesystem>
#endif
#if defined(TARGET_WINDOWS)
#include <process.h>
#define getpid() _getpid()
#else
#include <unistd.h>
#endif
#include "ipind_plugin.h"
#include "buffer_offload.h"
struct Buffer_offload_plugin
{
static constexpr int MAX_TOP_RANGES = 3;
struct Buffer_offload_plugin_state
{
std::ofstream outfile;
std::set< std::pair< uint8_t*, uint8_t* > > accesses;
void add_access(uint8_t* low, uint8_t* high)
{
static std::mutex mtx;
mtx.lock();
auto range = std::make_pair(low, high);
auto it_ge = accesses.lower_bound(range);
auto it_lt = (accesses.begin() != it_ge) ? std::prev(it_ge) : accesses.end();
if ((accesses.end() != it_ge) && (range.second + 1 >= it_ge->first))
{
range = std::make_pair(range.first, it_ge->second);
accesses.erase(it_ge);
}
if ((accesses.end() != it_lt) && (it_lt->second + 1 >= range.first))
{
range = std::make_pair(it_lt->first, range.second);
accesses.erase(it_lt);
}
accesses.insert(range);
mtx.unlock();
}
};
Buffer_offload_plugin_state* state_;
{
std::string outfilePath = (char*)rpcArgs[0].argData;
state_->outfile.open(outfilePath);
std::cout << "Log Open Requested for: " << outfilePath << std::endl;
retRpcArg->
argSchema = OPEN_OUT_FILE_SCHEMA.returnValueSchema;
retRpcArg->
argData = (uint64_t)(state_->outfile.is_open() && state_->outfile.good());
}
{
MEMREF* refs =
reinterpret_cast< MEMREF*
>(rpcArgs[0].
argData);
for (auto i = 0; i < count; i++)
{
auto low = refs[i].ea;
auto high = refs[i].ea + refs[i].size - 1;
state_->add_access(low, high);
}
retRpcArg->
argSchema = MEM_ANALYZE_SCHEMA.returnValueSchema;
}
{
if (!state_->accesses.empty())
{
auto maxClientTopRangeCount = (int)rpcArgs[2].argData;
rpcArgs[0].
argData = uintptr_t(state_->accesses.begin()->first);
rpcArgs[0].
argSchema = GET_MEM_ACCESS_INFO_SCHEMA.argSchemaArray[0];
rpcArgs[1].
argData = uintptr_t(state_->accesses.rbegin()->second);
rpcArgs[1].
argSchema = GET_MEM_ACCESS_INFO_SCHEMA.argSchemaArray[1];
auto maxTopRangesCount = maxClientTopRangeCount < MAX_TOP_RANGES ? maxClientTopRangeCount : MAX_TOP_RANGES;
rpcArgs[2].
argData = maxTopRangesCount;
size_t bufferSize = maxTopRangesCount * sizeof(MEMREF);
MEMREF* topRanges = (MEMREF*)malloc(bufferSize);
if (nullptr != topRanges)
{
memset(topRanges, 0, bufferSize);
for (auto& range : state_->accesses)
{
auto rangeSize = range.second - range.first + 1;
for (int i = 0; i < maxTopRangesCount; ++i)
{
if (rangeSize > topRanges[i].size)
{
for (int j = maxTopRangesCount - 1; j > i; --j)
{
topRanges[j] = topRanges[j - 1];
}
topRanges[i].ea = range.first;
topRanges[i].size = rangeSize;
break;
}
}
}
if (rpcArgs[3].deleter)
{
rpcArgs[3].
deleter(
reinterpret_cast< void*
>(rpcArgs[3].argData));
}
rpcArgs[3].
argData =
reinterpret_cast< uint64_t
>(topRanges);
rpcArgs[3].
argSchema = GET_MEM_ACCESS_INFO_SCHEMA.argSchemaArray[3];
}
}
retRpcArg->
argSchema = GET_MEM_ACCESS_INFO_SCHEMA.returnValueSchema;
}
void write_report()
{
if (state_->accesses.size() == 0) return;
std::map< size_t, uint32_t > blocks;
for (auto const& pair : state_->accesses)
{
auto accessSize = (pair.second - pair.first + 1);
blocks[accessSize]++;
}
state_->outfile << "Overall " << std::dec << state_->accesses.size() << " accesses to contiguous memory ranges."
<< std::endl;
state_->outfile << "Breakdown by contiguous range size:" << std::endl;
state_->outfile << std::left << std::setw(10) << "block size"
<< " | # blocks" << std::endl;
state_->outfile << "---------- | ----------" << std::endl;
for (auto const& block : blocks)
{
state_->outfile << std::left << std::setw(10) << std::dec << block.first << " | " << block.second << std::endl;
}
}
};
{
if (rpcId == OPEN_OUT_FILE_SCHEMA.rpcId)
{
return &OPEN_OUT_FILE_SCHEMA;
}
else if (rpcId == MEM_ANALYZE_SCHEMA.rpcId)
{
return &MEM_ANALYZE_SCHEMA;
}
else if (rpcId == GET_MEM_ACCESS_INFO_SCHEMA.rpcId)
{
return &GET_MEM_ACCESS_INFO_SCHEMA;
}
return nullptr;
}
{
auto this_ = reinterpret_cast< Buffer_offload_plugin* >(self);
if (rpcId == OPEN_OUT_FILE_SCHEMA.rpcId)
{
return this_->do_open_outfile(argCount, rpcArgs, retRpcArg);
}
else if (rpcId == MEM_ANALYZE_SCHEMA.rpcId)
{
return this_->do_analyze_mem_access(argCount, rpcArgs, retRpcArg);
}
else if (rpcId == GET_MEM_ACCESS_INFO_SCHEMA.rpcId)
{
return this_->do_get_mem_access_info(argCount, rpcArgs, retRpcArg);
}
}
bool init(
IPindPlugin* self,
int argc,
const char*
const argv[])
{
assert(2 == argc);
assert(std::string(argv[0]) == "-dummy-knob");
assert(std::string(argv[1]) == "1");
auto this_ = reinterpret_cast< Buffer_offload_plugin* >(self);
this_->state_ = new Buffer_offload_plugin::Buffer_offload_plugin_state;
return true;
}
{
plugin_log_verbose(self, "buffer offload plugin is being unloaded\n");
#if __has_include(<filesystem>)
std::string filename = "buffer_offload_plugin.log.";
filename += std::to_string(getpid());
if (!std::filesystem::exists(filename))
{
std::cerr << filename << " plugin log filename does not exists" << std::endl;
}
#endif
auto this_ = reinterpret_cast< Buffer_offload_plugin* >(self);
this_->write_report();
delete this_->state_;
}
{
static const char* OFFLOAD_PLUGIN_NAME = "buffer offload plugin";
if (0 == strncmp(OFFLOAD_PLUGIN_NAME, name, sizeof(OFFLOAD_PLUGIN_NAME)))
{
Buffer_offload_plugin* plugin = new (std::nothrow) Buffer_offload_plugin;
if (nullptr != plugin)
{
}
}
return nullptr;
}
E_plugin_type
The plugin type.
Definition ipind_plugin.h:36
void unload_plugin(IPindPlugin *plugin)
Release resources allocated allocated by load_plugin().
IPindPlugin * load_plugin(const char *name)
Allocate a structure object that can be safely casted to IPindPlugin. Set its pointers to the correct...
@ RPC
Indicates a pind RPC plugin.
Definition ipind_plugin.h:41
pinrt::std::enable_if_t< details::Rpc_arg_type_traits< RetType >::is_void, bool > do_rpc(Args &&... args) noexcept
Execute an RPC (Remote Procedure Call).
Definition pin_rpc_client.PH:527
uint8_t t_arg_count
Type of RPC argument count.
Definition rscprotomsgtypes.h:95
#define ARG_SCHEMA_SIZE(argSchema)
Get the size of an RPC argument schema.
Definition rscprotomsgtypes.h:335
uint32_t t_rpc_id
Type of RPC Ids.
Definition rscprotomsgtypes.h:88
@ RpcArgFlagsDataEmpty
Definition rscprotomsgtypes.h:75
@ RpcArgFlagsNone
Definition rscprotomsgtypes.h:73
Holds function pointers to basic common pind plugin functionality.
Definition ipind_plugin.h:55
void(* uninit)(IPindPlugin *self)
Perform any cleanup of resources allocated by init() or during the lifetime of the plugin.
Definition ipind_plugin.h:124
E_plugin_type(* get_plugin_type)(IPindPlugin *self)
Return plugin type.
Definition ipind_plugin.h:101
bool(* init)(IPindPlugin *self, int argc, const char *const argv[])
Called to initialize a plugin.
Definition ipind_plugin.h:115
Holds function pointers to pind RPC plugin functionality.
Definition ipind_plugin.h:139
IPindPlugin base_
Common pind plugin functionality (base class)
Definition ipind_plugin.h:144
void(* do_rpc)(IPindPlugin *self, t_rpc_id rpcId, t_arg_count argCount, t_rpc_arg *rpcArgs, t_rpc_ret *retRpcArg)
Execute the RPC request. Store result in the specified retRpcArg.
Definition ipind_plugin.h:188
t_rpc_message_schema const *(* get_rpc_schema)(IPindPlugin *self, t_rpc_id rpcId)
Get the schema for the given id if supported by the plugin.
Definition ipind_plugin.h:174
TStructure to hold the actual data to encode or the actual data of a decoded argument.
Definition rscprotomsgtypes.h:141
E_rpc_arg_flags flags
Encode/Decode flags for the argument.
Definition rscprotomsgtypes.h:177
size_t argDataSize
The size of data in/pointed to by argData.
Definition rscprotomsgtypes.h:161
t_rpc_arg_schema argSchema
A schema describing the argument.
Definition rscprotomsgtypes.h:172
void(* deleter)(void *)
Deleter function pointer to be used to release argument data.
Definition rscprotomsgtypes.h:167
uint64_t argData
The actual data of the argument.
Definition rscprotomsgtypes.h:156
A collection of RPC_arg_schema structures together describing an RPC message.
Definition rscprotomsgtypes.h:111