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.

472 lines
16 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.
*/
#include "ialarm.H"
#include "alarm_manager.H"
#include "parse_control.H"
#include "control_chain.H"
#include "alarms.H"
#include <iostream>
using namespace CONTROLLER;
set<ADDRINT> IALARM::_thread_first_ip;
ADDRINT IALARM::_threads_first_ip_vec[PIN_MAX_THREADS];
/// @cond INTERNAL_DOXYGEN
IALARM::IALARM(UINT32 tid ,UINT64 count,BOOL need_ctxt,
ALARM_MANAGER* manager){
_tid = tid;
_target_count._count = count;
_need_context = need_ctxt;
_alarm_manager = manager;
memset(_thread_count,0,sizeof(_thread_count));
memset(_armed,0,sizeof(_armed));
_global_armed = 0;
_activate_late_handler = FALSE;
_address = 0;
PIN_InitLock(&_lock);
static BOOL thread_callback_added = FALSE;
if (!thread_callback_added) {
PIN_CALLBACK thread_start = PIN_AddThreadStartFunction(ThreadStart, this);
// other tools working with the controller might do some initialization in their
// thread start callback.
// need to make sure we are been call AFTER all thread start callbacks were called.
CALLBACK_SetExecutionOrder(thread_start, CALL_ORDER_LAST);
}
}
VOID IALARM::InsertIfCall_Count(IALARM* alarm, INS ins, UINT32 ninst, IPOINT point){
if (alarm->HasGlobalCounter()) {
// Add global count instrumentation
INS_InsertIfCall(ins, point,
AFUNPTR(GlobalCount),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_ADDRINT, alarm,
IARG_THREAD_ID,
IARG_UINT32, ninst,
IARG_END);
}
else {
// Add per thread count instrumentation
INS_InsertIfCall(ins, point,
AFUNPTR(Count),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_ADDRINT, alarm,
IARG_THREAD_ID,
IARG_UINT32, ninst,
IARG_END);
}
}
VOID IALARM::InsertThenCall_Fire(IALARM* alarm, INS ins, IPOINT point){
if (alarm->_need_context){
INS_InsertThenCall(ins, point,
AFUNPTR(Fire),
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_ADDRINT, alarm,
IARG_CONTEXT,
IARG_INST_PTR,
IARG_THREAD_ID,
IARG_END);
}
else{
INS_InsertThenCall(ins, point,
AFUNPTR(Fire),
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_ADDRINT, alarm,
IARG_ADDRINT, static_cast<ADDRINT>(0), // pass a null as context,
IARG_INST_PTR,
IARG_THREAD_ID,
IARG_END);
}
}
// Insert late file instrumentation
VOID IALARM::Insert_LateInstrumentation(IALARM* alarm, INS ins){
// Check if late handler is set
if (!alarm->_alarm_manager->HasLateHandler())
return;
// Determine ipoint
IPOINT ipoint = IPOINT_AFTER;
if (INS_IsInterrupt(ins) || INS_IsSyscall(ins))
{
// We don't want the region of interest (in tracing)
// to include these instructions. Since they close
// the trace we can't take their next instruction,
// therefore don't deliver the late handler at all.
return;
}
if (INS_IsValidForIpointTakenBranch(ins))
{
ipoint = IPOINT_TAKEN_BRANCH;
}
// Add if-then analysis routines
INS_InsertIfCall(ins, ipoint,
AFUNPTR(ActivateLate),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetLateInstrumentOrder(),
IARG_ADDRINT, alarm,
IARG_THREAD_ID,
IARG_END);
INS_InsertThenCall(ins, ipoint,
AFUNPTR(LateFire),
IARG_CALL_ORDER, alarm->GetLateInstrumentOrder(),
IARG_ADDRINT, alarm,
IARG_CONST_CONTEXT,
IARG_INST_PTR,
IARG_THREAD_ID,
IARG_END);
}
// Thread specific Counter analysis routine
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::Count(IALARM* ialarm,
UINT32 tid,
UINT32 ninst){
UINT32 armed = ialarm->_armed[tid];
UINT32 correct_tid = (ialarm->_tid == tid) | (ialarm->_tid == ALL_THREADS);
UINT32 should_count = armed & correct_tid;
//if we are not in the correct thread
ialarm->_thread_count[tid]._count += ninst*(should_count);
// Return if we have reached thread counter
return should_count &
(ialarm->_thread_count[tid]._count >= ialarm->_target_count._count);
}
// Global counter analysis routine
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::GlobalCount(IALARM* ialarm,
UINT32 tid,
UINT32 ninst){
UINT32 should_count = ialarm->_global_armed;
// Increment global counter
ATOMIC::OPS::Increment<UINT64>(&ialarm->_global_count._count, ninst*(should_count));
// Return if we have global counter
return should_count &
(ialarm->_global_count._count >= ialarm->_target_count._count);
}
//we want to generate the context only when we really need it.
//that is way most of the code is in the If instrumentation.
//even if the If instrumentation is be not inlined.
VOID IALARM::Fire(IALARM* ialarm, CONTEXT* ctxt, VOID * ip, UINT32 tid){
// Check if flags was not already modified by another thread
// in interactive controller
if (ialarm->_alarm_manager->GetAlarmTypeFromManager() == ALARM_TYPE_INTERACTIVE)
{
ALARM_INTERACTIVE* interactive_alarm = static_cast<ALARM_INTERACTIVE*>(ialarm);
if (!interactive_alarm->GetListener()->CheckClearSignal())
return;
}
// Check if we need to activate late handler
// We should not activate it whenever this is precondition event
if (ialarm->_alarm_manager->HasLateHandler() && ialarm->_alarm_manager->GetEventType() != EVENT_PRECOND)
ialarm->_activate_late_handler = TRUE;
ialarm->_alarm_manager->Fire(ctxt, ip, tid);
}
// Late fire event
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::ActivateLate(IALARM* ialarm, UINT32 tid){
BOOL correct_tid = (ialarm->_tid == tid) | (ialarm->_tid == ALL_THREADS);
return ialarm->_activate_late_handler & correct_tid;
}
VOID IALARM::LateFire(IALARM* ialarm, CONTEXT* ctxt, VOID * ip, UINT32 tid) {
BOOL activate_late_fire = FALSE;
// Check if the late handler flag is set under lock
PIN_GetLock(&ialarm->_lock,0);
if (ialarm->_activate_late_handler)
{
ialarm->_activate_late_handler = FALSE;
activate_late_fire = TRUE;
}
PIN_ReleaseLock(&ialarm->_lock);
// Activate late fire
if (activate_late_fire)
ialarm->_alarm_manager->LateFire(ctxt, ip, tid);
}
VOID IALARM::Arm(){
PIN_GetLock(&_lock,0);
memset(_armed,1,sizeof(_armed));
_global_armed = 1;
PIN_ReleaseLock(&_lock);
}
VOID IALARM::Disarm(THREADID tid){
_armed[tid] = 0;
if (!_alarm_manager->IsVectorAlarm())
_thread_count[tid]._count = 0;
}
VOID IALARM::Disarm(){
PIN_GetLock(&_lock,0);
memset(_armed,0,sizeof(_armed));
memset(_thread_count,0,sizeof(_thread_count));
_global_count._count = 0;
_global_armed = 0;
PIN_ReleaseLock(&_lock);
}
BOOL IALARM::HasGlobalCounter() {
return _alarm_manager->HasGlobalCounter();
}
UINT32 IALARM::GetInstrumentOrder(){
return _alarm_manager->GetInsOrder();
}
UINT32 IALARM::GetLateInstrumentOrder(){
return _alarm_manager->GetLateInsOrder();
}
VOID IALARM::TraceAddress(TRACE trace, VOID* v)
{
IALARM* ialarm = static_cast<IALARM*>(v);
ADDRINT trace_addr = TRACE_Address(trace);
UINT32 trace_size = TRACE_Size(trace);
for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
{
// Check Target
// Get the last instruction in the BBL
INS ins = BBL_InsTail(bbl);
// Handle direct branches or calls
if ( INS_IsDirectControlFlow(ins) )
{
// Get the target and compare it to the address we need
ADDRINT target = INS_DirectControlFlowTargetAddress(ins);
if (target == ialarm->_address)
{
InsertIfCall_Count(ialarm, ins, 1, IPOINT_TAKEN_BRANCH);
InsertThenCall_Fire(ialarm, ins, IPOINT_TAKEN_BRANCH);
// Add late handler instrumentation if needed
Insert_LateInstrumentation(ialarm,ins);
}
}
// Handle indirect branches or calls
else if ( INS_IsIndirectControlFlow(ins) && !INS_IsXend(ins))
{
InsertIfCall_Target(ialarm, ins);
InsertThenCall_Fire(ialarm, ins, IPOINT_TAKEN_BRANCH);
// Add late handler instrumentation if needed
Insert_LateInstrumentation(ialarm,ins);
}
// If the address is not inside the trace then no need to check the
// instructions in the BBL
if (ialarm->_address < trace_addr || ialarm->_address > trace_addr+trace_size)
continue;
// Handle all other instructions in the BBL which may be before
// The instruction we are looking for
for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins))
{
// Compare the address of instructions itself
// This is to handle rare scenario of first ip in the thread
if (INS_Address(ins) == ialarm->_address &&
_thread_first_ip.find(ialarm->_address) != _thread_first_ip.end())
{
IPOINT ipoint = IPOINT_AFTER;
if (INS_IsValidForIpointTakenBranch(ins))
{
ipoint = IPOINT_TAKEN_BRANCH;
}
InsertIfCall_FirstIp(ialarm, ins, ipoint);
InsertThenCall_Fire(ialarm, ins, ipoint);
// Add late handler instrumentation if needed
Insert_LateInstrumentation(ialarm,ins);
}
// Only relevant for instructions will fall through path
if (!INS_HasFallThrough(ins))
return;
// Compare the address of the next instruction to check if we
// encountered the address of the alarm
if (INS_NextAddress(ins) == ialarm->_address)
{
InsertIfCall_Count(ialarm, ins, 1, IPOINT_AFTER);
InsertThenCall_Fire(ialarm, ins, IPOINT_AFTER);
// Add late handler instrumentation if needed
Insert_LateInstrumentation(ialarm,ins);
}
}
}
}
// Instrumentation of indirect branch checking
VOID IALARM::InsertIfCall_Target(IALARM* alarm, INS ins){
if (alarm->HasGlobalCounter())
INS_InsertIfCall(ins, IPOINT_TAKEN_BRANCH,
AFUNPTR(CheckTargetGlobal),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_PTR, alarm,
IARG_BRANCH_TARGET_ADDR,
IARG_END);
else
INS_InsertIfCall(ins, IPOINT_TAKEN_BRANCH,
AFUNPTR(CheckTarget),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_PTR, alarm,
IARG_THREAD_ID,
IARG_BRANCH_TARGET_ADDR,
IARG_END);
}
// Instrumentation of first ip address checking
VOID IALARM::InsertIfCall_FirstIp(IALARM* alarm, INS ins, IPOINT point){
if (alarm->HasGlobalCounter())
INS_InsertIfCall(ins, point,
AFUNPTR(CheckFirstIpGlobal),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_PTR, alarm,
IARG_THREAD_ID,
IARG_INST_PTR,
IARG_END);
else
INS_InsertIfCall(ins, point,
AFUNPTR(CheckFirstIp),
IARG_FAST_ANALYSIS_CALL,
IARG_CALL_ORDER, alarm->GetInstrumentOrder(),
IARG_PTR, alarm,
IARG_THREAD_ID,
IARG_INST_PTR,
IARG_END);
}
// Check if we have reached the target we need for global counter
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::CheckTarget(IALARM* ialarm,
THREADID tid,
ADDRINT branch_target) {
UINT32 armed = ialarm->_armed[tid];
UINT32 correct_tid = (ialarm->_tid == tid) | (ialarm->_tid == ALL_THREADS);
UINT32 should_count = armed & correct_tid & (ialarm->_address == branch_target);
// Increment counter if needed
ialarm->_thread_count[tid]._count += should_count;
// Return if we have reached thread or global counter
return should_count & (ialarm->_thread_count[tid]._count >= ialarm->_target_count._count);
}
// Check if we have reached the target we need
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::CheckTargetGlobal(IALARM* ialarm,
ADDRINT branch_target) {
UINT32 should_count = ialarm->_global_armed & (ialarm->_address == branch_target);
// Increment global counter
ATOMIC::OPS::Increment<UINT64>(&ialarm->_global_count._count, should_count);
// Return if we have reached thread or global counter
return should_count & (ialarm->_global_count._count >= ialarm->_target_count._count);
}
// Check if we have reached the target we need in the first ip of the thread
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::CheckFirstIp(IALARM* ialarm,
THREADID tid,
ADDRINT addr) {
UINT32 armed = ialarm->_armed[tid];
UINT32 correct_tid = (ialarm->_tid == tid) | (ialarm->_tid == ALL_THREADS);
UINT32 should_count = armed & correct_tid & (addr == _threads_first_ip_vec[tid]);
// Reset the vector value of this thread so that next time
// we will not count it and comparison to first ip will not return true.
_threads_first_ip_vec[tid] = 0;
// Increment counter if needed
ialarm->_thread_count[tid]._count += should_count;
// Return if we have reached thread or global counter
return should_count & (ialarm->_thread_count[tid]._count >= ialarm->_target_count._count);
}
// Check if we have reached the target we need in the first ip of the thread
// for global counting
ADDRINT PIN_FAST_ANALYSIS_CALL IALARM::CheckFirstIpGlobal(IALARM* ialarm,
THREADID tid,
ADDRINT addr) {
UINT32 should_count = ialarm->_global_armed & (addr == _threads_first_ip_vec[tid]);
// Reset the vector value of this thread so that next time
// we will not count it and comparison to first ip will not return true.
_threads_first_ip_vec[tid] = 0;
// Increment global counter
ATOMIC::OPS::Increment<UINT64>(&ialarm->_global_count._count, should_count);
// Return if we have reached thread or global counter
return should_count & (ialarm->_global_count._count >= ialarm->_target_count._count);
}
// Thread start callback
VOID IALARM::ThreadStart(THREADID tid, CONTEXT *ctxt, INT32 flags, VOID *v){
ADDRINT first_ip = PIN_GetContextReg(ctxt, REG_INST_PTR);
_thread_first_ip.insert(first_ip);
_threads_first_ip_vec[tid] = first_ip;
// this IP might be already instrumented, so we need to reset
// the instrumentations on it's BB.
PIN_RemoveInstrumentationInRange(first_ip, first_ip+15);
// Check if we need to fix thread id
// This is possible if thread translation callback is set
// This is only relevant if this is not a global counter because in global
// counters we summarize all threads and there is not meaning to tid.
IALARM* ialarm = static_cast<IALARM*>(v);
if (!ialarm->HasGlobalCounter() &&
ialarm->_alarm_manager->GetControlChain()->GetControlManager()->GetThreadTransCallback() != NULL)
{
THREADID trans_tid = (ialarm->_alarm_manager->GetControlChain()->GetControlManager()->GetThreadTransCallback())(tid,NULL);
if (trans_tid != tid && ialarm->_tid == trans_tid)
{
ialarm->_armed[tid] = TRUE;
ialarm->_armed[trans_tid] = FALSE;
ialarm->_tid = tid;
}
}
}
/// @endcond