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.

421 lines
14 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 <sstream>
#include "alarm_manager.H"
#include "controller_events.H"
#include "parse_control.H"
#include "alarms.H"
#include "pin.H"
using namespace CONTROLLER;
/// @cond INTERNAL_DOXYGEN
map<string, ALARM_TYPE> ALARM_MANAGER::InitAlarms(){
map<string, ALARM_TYPE> alarm_map;
alarm_map["icount"] = ALARM_TYPE_ICOUNT;
alarm_map["address"] = ALARM_TYPE_ADDRESS;
alarm_map["ssc"] = ALARM_TYPE_SSC;
alarm_map["itext"] = ALARM_TYPE_ITEXT;
alarm_map["int3"] = ALARM_TYPE_INT3;
alarm_map["isa_extension"] = ALARM_TYPE_ISA_EXTENSION;
alarm_map["isa_category"] = ALARM_TYPE_ISA_CATEGORY;
alarm_map["interactive"] = ALARM_TYPE_INTERACTIVE;
alarm_map["enter_func"] = ALARM_TYPE_ENTER_FUNC;
alarm_map["exit_func"] = ALARM_TYPE_EXIT_FUNC;
alarm_map["cpuid"] = ALARM_TYPE_CPUID;
alarm_map["magic"] = ALARM_TYPE_MAGIC;
alarm_map["pcontrol"] = ALARM_TYPE_PCONTROL;
alarm_map["timeout"] = ALARM_TYPE_TIMEOUT;
#if !defined(TARGET_WINDOWS)
alarm_map["signal"] = ALARM_TYPE_SIGNAL;
#endif
alarm_map["image_load"] = ALARM_TYPE_IMAGE_LOAD;
return alarm_map;
}
ALARM_TYPE ALARM_MANAGER::GetAlarmType(const string& alarm_name){
map<string, ALARM_TYPE>::iterator iter = _alarm_map.find(alarm_name);
if (iter == _alarm_map.end()){
ASSERT(FALSE,"Unsupported alarm: " + alarm_name);
}
return iter->second;
}
ALARM_MANAGER::ALARM_MANAGER(const string& control_str,
CONTROL_CHAIN* control_chain,
UINT32 id,
BOOL late_handler,
BOOL vector_alarm,
UINT32 vector_index){
_bcast = FALSE;
_tid = ALL_THREADS;
_count = 1;
_global_count = FALSE;
_raw_alarm = control_str;
_control_chain = control_chain;
_uniform_type = FALSE;
_arm_next = TRUE;
_id = id;
_alarm_map = InitAlarms();
_late_handler = late_handler;
_vector_alarm = vector_alarm;
_vector_index = vector_index;
_uniform_count = 0;
_uniform_period = 0;
_uniform_length = 0;
vector<string> control_tokens;
PARSER::SplitArgs(":",control_str,control_tokens);
if (PARSER::UniformToken(control_tokens)){
ParseUniform(control_tokens);
ParseCommon(control_tokens);
_ialarm = GenUniform();
}
else{
ParseEventId(control_tokens);
ParseAlarm(control_tokens);
ParseCommon(control_tokens);
// In vector alarms we do not create all the alarms
// but the first one in order to solve performance issues
// in iregions
if (!_vector_alarm || _vector_index==0)
_ialarm = GenerateAlarm();
else
_ialarm = NULL;
}
}
IALARM* ALARM_MANAGER::GenUniform(){
BOOL ctxt = _control_chain->NeedContext();
//FIXME: add comment
return new ALARM_ICOUNT("1",_tid,1,ctxt,this);
}
IALARM* ALARM_MANAGER::GenAddress(){
string hex = "0x";
BOOL ctxt = _control_chain->NeedContext();
if (_alarm_value.compare(0, 2, hex) == 0){
//this is a raw address
return new ALARM_ADDRESS(_alarm_value,_tid,_count,ctxt,this);
}
if (_alarm_value.find("+",0) == string::npos){
//this is a symbol
return new ALARM_SYMBOL(_alarm_value,_tid,_count,ctxt,this);
}
else{
vector<string> tokens;
PARSER::SplitArgs("+",_alarm_value,tokens);
return new ALARM_IMAGE(tokens[0],tokens[1],_tid,_count,ctxt,this);
}
}
IALARM* ALARM_MANAGER::GenerateAlarm(){
BOOL ctxt = _control_chain->NeedContext();
switch (_alarm_type)
{
case ALARM_TYPE_ICOUNT: return new ALARM_ICOUNT(_alarm_value,_tid,_count,
ctxt,this);
case ALARM_TYPE_ADDRESS: return GenAddress();
case ALARM_TYPE_SSC: return new ALARM_SSC(_alarm_value,_tid,_count,
ctxt,this);
case ALARM_TYPE_ISA_EXTENSION: return new ALARM_ISA_EXTENSION(_alarm_value,
_tid,_count,ctxt,this);
case ALARM_TYPE_ISA_CATEGORY: return new ALARM_ISA_CATEGORY(_alarm_value,
_tid,_count,
ctxt,this);
case ALARM_TYPE_ITEXT: return new ALARM_ITEXT(_alarm_value,_tid,_count,ctxt,
this);
case ALARM_TYPE_INT3: return new ALARM_INT3(_alarm_value,_tid,_count,ctxt,
this);
case ALARM_TYPE_INTERACTIVE: return new ALARM_INTERACTIVE(_tid,ctxt,this);
case ALARM_TYPE_ENTER_FUNC: return new ALARM_ENTER_FUNC(_alarm_value,_tid,
_count,ctxt,this);
case ALARM_TYPE_EXIT_FUNC: return new ALARM_EXIT_FUNC(_alarm_value,_tid,
_count,ctxt,this);
case ALARM_TYPE_CPUID: return new ALARM_CPUID(_alarm_value,_tid,_count,
ctxt,this);
case ALARM_TYPE_MAGIC: return new ALARM_MAGIC(_alarm_value,_tid,_count,
ctxt,this);
case ALARM_TYPE_PCONTROL: return new ALARM_PCONTROL(_alarm_value,_tid,
_count,ctxt,this);
case ALARM_TYPE_TIMEOUT: return new ALARM_TIMEOUT(_alarm_value,_tid,
_count,ctxt,this);
case ALARM_TYPE_SIGNAL: return new ALARM_SIGNAL(_alarm_value,_tid,
_count,ctxt,this);
case ALARM_TYPE_IMAGE_LOAD: return new ALARM_IMAGE_LOAD(_alarm_value, _tid,
_count, ctxt, this);
default: ASSERT(FALSE,"Unexcpected alarm");
}
return NULL; //pacify the compiler
}
VOID ALARM_MANAGER::ParseUniform(vector<string>& control_tokens){
ASSERT(control_tokens.size() > 3,"usage uniform:<period>:<length>:<count>[:<tid>]");
UINT32 uniform_tokens_num = 4;
_event_name = "uniform";
_event_type = EVENT_START;
_uniform_type = TRUE;
_arm_next = FALSE;
_uniform_period = PARSER::StringToUint64(control_tokens[1]);
_uniform_length = PARSER::StringToUint64(control_tokens[2]);
_uniform_count = PARSER::StringToUint64(control_tokens[3]);
// Check if the next token is thread id
if (control_tokens.size() > 4 && control_tokens[4].substr(0,3) == "tid")
{
// We found thread id
_tid = PARSER::StringToUint32(control_tokens[4].substr(3,control_tokens[4].length()));
uniform_tokens_num = 5;
}
control_tokens.erase(control_tokens.begin(),control_tokens.begin()+uniform_tokens_num);
ASSERT(_uniform_period >= _uniform_length,"uniform period must be "
"larger than uniform length");
_control_chain->SetUniformAlarm(this);
}
VOID ALARM_MANAGER::ParseEventId(vector<string>& control_tokens){
ASSERT(control_tokens.size() > 0,"Usage: no event");
_event_name = control_tokens[0];
_event_type = _control_chain->EventStringToType(_event_name);
control_tokens.erase(control_tokens.begin());
}
VOID ALARM_MANAGER::ParseAlarm(vector<string>& control_tokens){
_alarm_name = control_tokens[0];
_alarm_type = GetAlarmType(_alarm_name);
// Only icount alarm can be vector
if (_alarm_type != ALARM_TYPE_ICOUNT && _vector_alarm)
{
ASSERT(FALSE,"Only icount alarm can have vector alarm flag set");
}
if (_alarm_type == ALARM_TYPE_INTERACTIVE){
control_tokens.erase(control_tokens.begin());
return;
}
ASSERT(control_tokens.size() > 1,"Usage Error: Alarm with no value");
control_tokens.erase(control_tokens.begin());
_alarm_value = control_tokens[0];
control_tokens.erase(control_tokens.begin());
// Parse global icount value
if (_global_count && _vector_alarm)
{
_icount_alarm_value = PARSER::StringToUint64(_alarm_value);
}
}
VOID ALARM_MANAGER::ParseCommon(vector<string>& control_tokens){
BOOL used_tid = FALSE;
BOOL used_count = FALSE;
BOOL used_global = FALSE;
for (UINT32 i = 0; i < control_tokens.size(); i++){
string token = control_tokens[i];
BOOL found_tid = PARSER::ParseTIDToken(token, &_tid);
BOOL found_bcast = PARSER::ParseBcastToken(token, &_bcast);
BOOL found_count = PARSER::ParseCountToken(token, &_count);
BOOL found_global = PARSER::ParseGlobalToken(token, &_global_count);
BOOL found_repeat = PARSER::ParseRepeatToken(token);
// Check if we got illegal token
// ParseCommon is called from the constructor of ALARM_MANAGER
// and after the event id and alarm value tokens where parsed.
// When we have reached then we parse all remaining tokens.
ASSERT(found_tid || found_bcast || found_count || found_global || found_repeat,
"Usage: redundant token: " + control_tokens[i] );
if (found_tid) used_tid = TRUE;
if (found_count) used_count = TRUE;
if (found_global) used_global = TRUE;
}
// global used only with count token
if (used_global) {
ASSERT(used_count || _alarm_type == ALARM_TYPE_ICOUNT,
"Usage: global token must be used only with count token or icount alarm type");
}
// tid and global tokens are illegal together
ASSERT(!used_tid || !used_global ,"Usage: can not use both tid and global tokens");
}
VOID ALARM_MANAGER::SetNextUniformEvent(THREADID tid){
if (_event_type == EVENT_START){
_event_type = EVENT_STOP;
_ialarm->SetCount(_uniform_length);
_ialarm->Arm();
return;
}
if (_event_type == EVENT_STOP){
//completed one uniform cycle
_uniform_count--;
if (_uniform_count == 0){
//completed all uniform cycles, we can now arm the next alarm
//in the chain(if exists)
_arm_next = TRUE;
}
else{
_event_type = EVENT_START;
//setting the icount for the region until we get to "start"
_ialarm->SetCount(_uniform_period - _uniform_length);
_ialarm->Arm();
}
return;
}
}
VOID ALARM_MANAGER::Fire(CONTEXT* ctx, VOID * ip, THREADID tid){
if (!Disarm(tid))
return;
EVENT_TYPE ev = _event_type;
BOOL bcast = _bcast;
//saving the current event type since SetNextEvent will modify it.
//must do SetNextEvent before Fire since its side effects are needed in
//the fire function
if (_uniform_type && !IsUniformDone()){
//for uniform we reset all the counters
Disarm();
bcast = TRUE;
SetNextUniformEvent(tid);
}
_control_chain->Fire(ev,ctx,ip,tid,bcast, _id);
// Handle vector alarms
// Whenever there is a Fire event for an alarm which is a part of
// vector alarms we reuse this alarm for other alarm managers.
// This is done in order to solve performance issues.
// Currently it is supported only for icount alarms.
// We do the following actions:
// 1. Get the next alarm manager in the vector from the chain
// 2. Update the icount alarm with the event data from the alarm manager
// 3. Connect the alarm manager to the original icount alarm
// 4. Arm the icount alarm so that it will Fire on the next icount
// 5. Remove the connection from current alarm manager which fired from the icount alarm
if (_vector_alarm)
{
// Update the alarm with the data from next alarm manager and arm it
UINT32 tid_for_next_alarm = _tid;
if (_global_count)
tid_for_next_alarm = 0;
ALARM_MANAGER * next_alarm = _control_chain->GetNextAlaramManager(_vector_index,tid_for_next_alarm);
if (next_alarm)
{
_ialarm->UpdateAlarm(next_alarm,next_alarm->_alarm_value);
next_alarm->_ialarm = _ialarm;
if (_global_count)
next_alarm->ArmAll();
else
next_alarm->ArmTID(_tid);
_ialarm = NULL;
}
}
}
// Late fire
VOID ALARM_MANAGER::LateFire(CONTEXT* ctx, VOID * ip, THREADID tid){
_control_chain->LateFire(_event_type,ctx,ip,tid,_bcast, _id);
}
BOOL ALARM_MANAGER::HasStartEvent(){
return (_event_type == EVENT_START);
}
VOID ALARM_MANAGER::Print(){
cerr << "EVENT: " << _event_name <<endl;
cerr << "ALARM: " << _alarm_name <<endl;
cerr << "VALUE: " << _alarm_value <<endl;
cerr << "TID: " << _tid <<endl;
cerr << "BCAST:" << _bcast <<endl;
cerr << "COUNT:" << _count <<endl;
cerr << "GLOBAL COUNT:" << _global_count <<endl;
}
BOOL ALARM_MANAGER::IsUniformDone(){
return _uniform_count == 0;
}
VOID ALARM_MANAGER::Disarm(){
_ialarm->Disarm();
}
BOOL ALARM_MANAGER::Disarm(THREADID tid){
if (_bcast || _global_count){
// If the alarm is global then there is a chance that another thread
// already disarmed it
if (_global_count)
{
// Check if this is a race condition and we did not reach
// the needed global count
if (_vector_alarm && _icount_alarm_value > _ialarm->GetGlobalCount())
{
return FALSE;
}
BOOL armed = _ialarm->DisarmGlobalArmed();
return armed;
}
_ialarm->Disarm();
return TRUE;
}
else{
_ialarm->Disarm(tid);
return TRUE;
}
}
VOID ALARM_MANAGER::Activate(){
if (_tid == ALL_THREADS || _global_count){
ArmAll();
}
else{
ArmTID(_tid);
}
}
/// @endcond