You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
9.8 KiB
296 lines
9.8 KiB
/*
|
|
* Copyright 2002-2019 Intel Corporation.
|
|
*
|
|
* This software is provided to you as Sample Source Code as defined in the accompanying
|
|
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
|
|
* section 1.L.
|
|
*
|
|
* This software and the related documents are provided as is, with no express or implied
|
|
* warranties, other than those that are expressly stated in the License.
|
|
*/
|
|
|
|
/*
|
|
* This tool is intended to run with the test applications "thread-unix.cpp"
|
|
* or "thread-windows.cpp". It raises a PinADX tool breakpoint whenever a
|
|
* thread calls the GlobalFunction() function. There is also a knob that
|
|
* will ensure that all threads report their breakpoint at the same time.
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include "pin.H"
|
|
#include "atomic.hpp"
|
|
#include "tool_macros.h"
|
|
|
|
KNOB<unsigned> KnobSyncForThreads(KNOB_MODE_WRITEONCE, "pintool",
|
|
"sync_for_threads", "0",
|
|
"Add synchronization to ensure that all tool breakpoints happen at the same time. "
|
|
"The argument to this knob tells the number of threads in the application "
|
|
"that will call the GlobalFunction() function.");
|
|
KNOB<BOOL> KnobWaitForDebugger(KNOB_MODE_WRITEONCE, "pintool",
|
|
"wait_for_debugger", "0",
|
|
"If no debugger is connected, wait for one to connect when the breakpoints trigger.");
|
|
KNOB<std::string> KnobPort(KNOB_MODE_WRITEONCE, "pintool",
|
|
"port", "", "Output file where TCP information is written");
|
|
KNOB<BOOL> KnobUseIargConstContext(KNOB_MODE_WRITEONCE, "pintool",
|
|
"const_context", "0", "use IARG_CONST_CONTEXT");
|
|
|
|
static bool OnCommand(THREADID, CONTEXT *, const std::string &, std::string *, VOID *);
|
|
static BOOL ParseThreadId(const std::string &, THREADID *);
|
|
static void InstrumentRtn(RTN, VOID *);
|
|
static void DoBreakpoint(THREADID, CONTEXT *);
|
|
static ADDRINT ClearSkipPc();
|
|
static void OnExit(INT32, VOID *);
|
|
|
|
static REG RegSkipOne;
|
|
static BOOL FoundWorker = FALSE;
|
|
static volatile unsigned BreakpointCount = 0;
|
|
static PIN_SEMAPHORE ReadyToDoBreakpoint;
|
|
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
PIN_Init(argc, argv);
|
|
PIN_InitSymbols();
|
|
PIN_SemaphoreInit(&ReadyToDoBreakpoint);
|
|
RegSkipOne = PIN_ClaimToolRegister();
|
|
if (!REG_valid(RegSkipOne))
|
|
{
|
|
std::cerr << "Unable to allocate tool register" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
if (!KnobPort.Value().empty())
|
|
{
|
|
DEBUG_CONNECTION_INFO info;
|
|
if (!PIN_GetDebugConnectionInfo(&info))
|
|
{
|
|
std::cerr << "Error from PIN_GetDebugConnectionInfo()" << std::endl;
|
|
return 1;
|
|
}
|
|
if (info._type != DEBUG_CONNECTION_TYPE_TCP_SERVER)
|
|
{
|
|
std::cerr << "Unexpected connection type from PIN_GetDebugConnectionInfo()" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
std::ofstream out(KnobPort.Value().c_str());
|
|
out << std::dec << info._tcpServer._tcpPort;
|
|
}
|
|
|
|
PIN_AddDebugInterpreter(OnCommand, 0);
|
|
RTN_AddInstrumentFunction(InstrumentRtn, 0);
|
|
PIN_AddFiniFunction(OnExit, 0);
|
|
PIN_StartProgram();
|
|
return 0;
|
|
}
|
|
|
|
static bool OnCommand(THREADID, CONTEXT *, const std::string &cmd, std::string *reply, VOID *)
|
|
{
|
|
if (cmd == "list-pending")
|
|
{
|
|
unsigned nPendingToolbreaks = 0;
|
|
UINT32 nThreads = PIN_GetStoppedThreadCount();
|
|
for (UINT32 i = 0; i < nThreads; i++)
|
|
{
|
|
THREADID tid = PIN_GetStoppedThreadId(i);
|
|
if (PIN_GetStoppedThreadPendingToolBreakpoint(tid, 0))
|
|
nPendingToolbreaks++;
|
|
}
|
|
|
|
std::ostringstream os;
|
|
os << "There are " << std::dec << nPendingToolbreaks << " pending breakpoints:\n";
|
|
for (UINT32 i = 0; i < nThreads; i++)
|
|
{
|
|
std::string msg;
|
|
THREADID tid = PIN_GetStoppedThreadId(i);
|
|
if (PIN_GetStoppedThreadPendingToolBreakpoint(tid, &msg))
|
|
os << " " << std::dec << tid << ": " << msg;
|
|
}
|
|
*reply = os.str();
|
|
return true;
|
|
}
|
|
|
|
if (cmd.find("squash-pending ") == 0)
|
|
{
|
|
std::string what = cmd.substr(sizeof("squash-pending ")-1);
|
|
unsigned nSquashed = 0;
|
|
|
|
THREADID tid = 0;
|
|
if (what == "all")
|
|
{
|
|
UINT32 nThreads = PIN_GetStoppedThreadCount();
|
|
for (UINT32 i = 0; i < nThreads; i++)
|
|
{
|
|
tid = PIN_GetStoppedThreadId(i);
|
|
if (PIN_ChangePendingToolBreakpointOnStoppedThread(tid, TRUE, ""))
|
|
nSquashed++;
|
|
}
|
|
}
|
|
else if (ParseThreadId(what, &tid))
|
|
{
|
|
if (PIN_ChangePendingToolBreakpointOnStoppedThread(tid, TRUE, ""))
|
|
nSquashed++;
|
|
}
|
|
else
|
|
{
|
|
*reply = "Invalid parameter '" + what + "'\n";
|
|
return true;
|
|
}
|
|
|
|
std::ostringstream os;
|
|
os << "Squashed " << std::dec << nSquashed << " breakpoints\n";
|
|
*reply = os.str();
|
|
return true;
|
|
}
|
|
|
|
if (cmd.find("append-pending ") == 0)
|
|
{
|
|
std::string rest = cmd.substr(sizeof("append-pending ")-1);
|
|
size_t pos = rest.find(' ');
|
|
std::string what = (pos == std::string::npos) ? "" : rest.substr(0, pos);
|
|
std::string append = (pos == std::string::npos) ? "" : rest.substr(pos+1);
|
|
if (!append.empty() && append[append.size()-1] == '\n')
|
|
append.erase(append.size()-1, 1);
|
|
|
|
unsigned nChanged = 0;
|
|
THREADID tid = 0;
|
|
if (what == "all")
|
|
{
|
|
UINT32 nThreads = PIN_GetStoppedThreadCount();
|
|
for (UINT32 i = 0; i < nThreads; i++)
|
|
{
|
|
tid = PIN_GetStoppedThreadId(i);
|
|
std::string msg;
|
|
if (PIN_GetStoppedThreadPendingToolBreakpoint(tid, &msg))
|
|
{
|
|
size_t pos = msg.size();
|
|
if (!msg.empty() && msg[pos-1] == '\n')
|
|
pos--;
|
|
msg.insert(pos, " " + append);
|
|
if (PIN_ChangePendingToolBreakpointOnStoppedThread(tid, FALSE, msg))
|
|
nChanged++;
|
|
}
|
|
}
|
|
}
|
|
else if (ParseThreadId(what, &tid))
|
|
{
|
|
std::string msg;
|
|
if (PIN_GetStoppedThreadPendingToolBreakpoint(tid, &msg))
|
|
{
|
|
size_t pos = msg.size();
|
|
if (!msg.empty() && msg[pos-1] == '\n')
|
|
pos--;
|
|
msg.insert(pos, " " + append);
|
|
if (PIN_ChangePendingToolBreakpointOnStoppedThread(tid, FALSE, msg))
|
|
nChanged++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*reply = "Invalid parameter '" + what + "'\n";
|
|
return true;
|
|
}
|
|
|
|
std::ostringstream os;
|
|
os << "Changed " << std::dec << nChanged << " breakpoints\n";
|
|
*reply = os.str();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static BOOL ParseThreadId(const std::string &str, THREADID *val)
|
|
{
|
|
std::istringstream is(str);
|
|
|
|
THREADID id;
|
|
is >> std::dec >> id;
|
|
if (is.fail() || !is.eof())
|
|
return FALSE;
|
|
*val = id;
|
|
return TRUE;
|
|
}
|
|
|
|
static void InstrumentRtn(RTN rtn, VOID *)
|
|
{
|
|
if (RTN_Name(rtn) == C_MANGLE("GlobalFunction"))
|
|
{
|
|
RTN_Open(rtn);
|
|
INS ins = RTN_InsHeadOnly(rtn);
|
|
INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(DoBreakpoint), IARG_THREAD_ID,
|
|
(KnobUseIargConstContext)?IARG_CONST_CONTEXT:IARG_CONTEXT,
|
|
// IARG_CONST_CONTEXT has much lower overhead
|
|
// than IARG_CONTEX for passing the CONTEXT*
|
|
// to the analysis routine. Note that IARG_CONST_CONTEXT
|
|
// passes a read-only CONTEXT* to the analysis routine
|
|
IARG_END);
|
|
if (INS_IsValidForIpointAfter(ins))
|
|
INS_InsertCall(ins, IPOINT_AFTER, AFUNPTR(ClearSkipPc), IARG_RETURN_REGS, RegSkipOne, IARG_END);
|
|
if (INS_IsValidForIpointTakenBranch(ins))
|
|
INS_InsertCall(ins, IPOINT_TAKEN_BRANCH, AFUNPTR(ClearSkipPc), IARG_RETURN_REGS, RegSkipOne, IARG_END);
|
|
FoundWorker = TRUE;
|
|
RTN_Close(rtn);
|
|
}
|
|
}
|
|
|
|
static void DoBreakpoint(THREADID tid, CONTEXT *context)
|
|
{
|
|
CONTEXT writableContext, *ctxt;
|
|
if (KnobUseIargConstContext)
|
|
{ // need to copy the ctxt into a writable context
|
|
PIN_SaveContext(context, &writableContext);
|
|
ctxt = &writableContext;
|
|
}
|
|
else
|
|
{
|
|
ctxt = context;
|
|
}
|
|
|
|
ADDRINT skipPc = PIN_GetContextReg(ctxt, RegSkipOne);
|
|
ADDRINT pc = PIN_GetContextReg(ctxt, REG_INST_PTR);
|
|
if (skipPc == pc)
|
|
return;
|
|
PIN_SetContextReg(ctxt, RegSkipOne, pc);
|
|
|
|
// If this knob is specified, add synchronization that ensures that each thread
|
|
// reports the tool breakpoint at the same time. Synchonization like this is
|
|
// not generally safe, but this tool is only run on a simple test program, and
|
|
// the synchronization is safe in that case.
|
|
//
|
|
if (KnobSyncForThreads.Value())
|
|
{
|
|
if (ATOMIC::OPS::Increment<unsigned>(&BreakpointCount, 1) + 1 >= KnobSyncForThreads.Value())
|
|
{
|
|
PIN_SemaphoreSet(&ReadyToDoBreakpoint);
|
|
if (KnobWaitForDebugger.Value())
|
|
std::cout << "Waiting for debugger to attach" << std::endl;
|
|
}
|
|
PIN_SemaphoreWait(&ReadyToDoBreakpoint);
|
|
}
|
|
|
|
// When the process stops in the debugger, all threads will be simultaneously
|
|
// stopped here.
|
|
//
|
|
std::ostringstream os;
|
|
os << "Stopping in Worker Thread " << std::dec << tid << "\n";
|
|
PIN_ApplicationBreakpoint(ctxt, tid, KnobWaitForDebugger.Value(), os.str());
|
|
}
|
|
|
|
static ADDRINT ClearSkipPc()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void OnExit(INT32, VOID *)
|
|
{
|
|
if (!FoundWorker)
|
|
{
|
|
std::cout << "FAILURE: Couldn't add instrumentation routines" << std::endl;
|
|
std::exit(1);
|
|
}
|
|
}
|