T32SIM: Integrating Tracing feature of the T32SIM.
After each simulator break, T32Tracer retrieves the latest (16) trace records from the T32. Memory address and value can now be evaluated easily from the trace record. TODO:Nevertheless we still have to traverse the trace to find the instruction causing the access.
This commit is contained in:
@ -1 +1 @@
|
||||
add_library(t32api hlinknet.cc hremote.cc)
|
||||
add_library(t32api hlinknet.c hremote.c)
|
||||
|
||||
@ -1110,7 +1110,7 @@ int T32_Break(void) /* Stop Realtime */
|
||||
* NAME commandline
|
||||
*/
|
||||
|
||||
int T32_Cmd(char *name) /* Executes a command line */
|
||||
int T32_Cmd(const char *name) /* Executes a command line */
|
||||
{
|
||||
word wlen;
|
||||
int len;
|
||||
76
debuggers/t32/include/T32TraceFormat.hpp
Normal file
76
debuggers/t32/include/T32TraceFormat.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef __T32TRACEFORMAT_H__
|
||||
#define __T32TRACEFORMAT_H__
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
namespace fail {
|
||||
|
||||
struct T32TraceHeader {
|
||||
uint8_t export_file_header_string[32];
|
||||
uint8_t reserved_32;
|
||||
uint8_t cpu_code;
|
||||
uint8_t timestamp_available;
|
||||
uint8_t prestore_mode;
|
||||
uint8_t trigger_unit_available;
|
||||
uint8_t port_analyzer_avaiable_mode;
|
||||
uint8_t analyzer_type;
|
||||
uint8_t reserved_39;
|
||||
|
||||
uint32_t record_length; // in bytes
|
||||
uint32_t number_of_records; // in the file
|
||||
uint32_t last_record;
|
||||
uint32_t referenced_record;
|
||||
uint8_t reserved_56_63[8];
|
||||
};
|
||||
|
||||
struct T32TraceRecord {
|
||||
struct {
|
||||
uint32_t data_cycle :1; // bit 0
|
||||
uint32_t program_cycle :1; // bit 1
|
||||
uint32_t reserved_2_5 : 4; // bits 2-5
|
||||
uint32_t write_cycle : 1; // bit 6
|
||||
uint32_t reserved_7_20 : 14 ;// bits 7-20
|
||||
uint32_t flow_error : 1; // bit 21
|
||||
uint32_t reserved_22_24 : 3; //bits 22-24
|
||||
uint32_t fifo_overflow : 1; // bit 25
|
||||
uint32_t reserved_26_30 : 5; // bits 26-30
|
||||
uint32_t ownership_cycle :1; // bit 31
|
||||
} cycle_information;
|
||||
|
||||
// data byte enable mask
|
||||
uint8_t data_byte_valid; // bit0 : byte0 valid, ...
|
||||
|
||||
struct {
|
||||
uint8_t exec_signal : 1;
|
||||
uint8_t thumb_mode : 1;
|
||||
uint8_t arm_mode : 1;
|
||||
uint8_t reserved_3_4 : 2;
|
||||
uint8_t not_executed : 1;
|
||||
uint8_t executed : 1;
|
||||
uint8_t reserved_6_7 : 1;
|
||||
} cpu_info;
|
||||
|
||||
uint8_t reserved_6;
|
||||
|
||||
uint8_t core_number; // only on SMP targets
|
||||
uint32_t bus_data_address; // bus/data
|
||||
uint32_t pflow_address; // or upper part
|
||||
uint64_t data;
|
||||
uint64_t timestamp; // relative to ZERO in ns
|
||||
|
||||
bool isDataAccess(void) const { return cycle_information.data_cycle == 1; };
|
||||
bool isProgram(void) const { return cycle_information.program_cycle == 1; };
|
||||
bool isDataRead(void) const { return cycle_information.data_cycle && !cycle_information.write_cycle; };
|
||||
bool isDataWrite(void) const { return cycle_information.data_cycle && cycle_information.write_cycle; };
|
||||
uint32_t getAddress(void) const { return bus_data_address; };
|
||||
uint32_t getData(void) const { return data; };
|
||||
|
||||
|
||||
};
|
||||
|
||||
//<! This allows to print a record via Logger or cout
|
||||
std::ostream& operator <<(std::ostream & os, const fail::T32TraceRecord & r);
|
||||
|
||||
};
|
||||
|
||||
#endif // __T32TRACEFORMAT_H__
|
||||
|
||||
44
debuggers/t32/include/T32Tracer.hpp
Normal file
44
debuggers/t32/include/T32Tracer.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef __T32TRACER_HPP__
|
||||
#define __T32TRACER_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "T32TraceFormat.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
class T32Tracer {
|
||||
private:
|
||||
typedef std::vector<T32TraceRecord> record_vector_t;
|
||||
|
||||
bool avail;
|
||||
std::string path;
|
||||
std::string exportcmd;
|
||||
record_vector_t rvec;
|
||||
fail::Logger m_log;
|
||||
|
||||
public:
|
||||
typedef record_vector_t::const_reverse_iterator const_record_iterator;
|
||||
|
||||
T32Tracer( const std::string& tracefile );
|
||||
|
||||
void setup(void) const;
|
||||
int evaluate();
|
||||
|
||||
bool wasDataAccess(void) const ;
|
||||
const T32TraceRecord & getLatestRecord(void) const { return rvec.back(); };
|
||||
void dump(void);
|
||||
|
||||
bool available(void) const { return avail; };
|
||||
|
||||
// We return a reverse operator, as the trace list begins with the oldest record.
|
||||
// We just let it look like a "normal" iterator and traverse backwards starting
|
||||
// with most recent record. (Rbegin/Rend!)
|
||||
const_record_iterator begin() const { return rvec.rbegin(); };
|
||||
const_record_iterator end() const { return rvec.rend(); };
|
||||
};
|
||||
|
||||
}; // end of namespace
|
||||
#endif // __T32TRACER_HPP__
|
||||
|
||||
@ -315,7 +315,7 @@ T32EXTERN int T32_SetMode(int);
|
||||
T32EXTERN int T32_Go(void);
|
||||
T32EXTERN int T32_Break(void);
|
||||
T32EXTERN int T32_Terminate(int retval);
|
||||
T32EXTERN int T32_Cmd( char * );
|
||||
T32EXTERN int T32_Cmd( const char * );
|
||||
T32EXTERN int T32_CmdWin( dword, char * );
|
||||
T32EXTERN int T32_EvalGet ( dword * );
|
||||
T32EXTERN int T32_GetMessage ( char *, word * );
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
include_directories(include)
|
||||
|
||||
add_subdirectory(${T32_ARCHITECTURE})
|
||||
add_subdirectory(memlogger)
|
||||
|
||||
@ -9,21 +9,31 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
simulWord infoBase;
|
||||
int bustype;
|
||||
int data;
|
||||
}
|
||||
MemLog_t;
|
||||
|
||||
|
||||
|
||||
static int SIMULAPI readCB(simulProcessor processor, simulCallbackStruct * cbs, simulPtr private)
|
||||
{
|
||||
|
||||
// simulWord address = cbs->x.bus.address;
|
||||
simulWord address = cbs->x.bus.address;
|
||||
MemLog_t * memlog = (MemLog_t*)private;
|
||||
cbs->x.bus.data = memlog->data;
|
||||
//int width = cbs->x.bus.width;
|
||||
SIMUL_Printf(processor, "MEM Read *0x%x - 0x%x\n", cbs->x.bus.address, memlog->data);
|
||||
simulWord width = cbs->x.bus.width;
|
||||
SIMUL_Printf(processor, "MEM Read *0x%x - 0x%x\n", address, memlog->data);
|
||||
|
||||
simulWord data = memlog->data;
|
||||
simulWord writeAccess = 0;
|
||||
simulWord iadr = memlog->infoBase;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &address);
|
||||
iadr += 4;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &data);
|
||||
iadr += 4;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &width);
|
||||
iadr += 4;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &writeAccess);
|
||||
|
||||
return SIMUL_MEMORY_OK;
|
||||
}
|
||||
|
||||
@ -31,11 +41,23 @@ static int SIMULAPI readCB(simulProcessor processor, simulCallbackStruct * cbs,
|
||||
static int SIMULAPI writeCB(simulProcessor processor, simulCallbackStruct * cbs, simulPtr private)
|
||||
{
|
||||
simulWord data = cbs->x.bus.data;
|
||||
//simulWord address = cbs->x.bus.address;
|
||||
simulWord address = cbs->x.bus.address;
|
||||
MemLog_t * memlog = (MemLog_t*)private;
|
||||
memlog->data = cbs->x.bus.data;
|
||||
//int width = cbs->x.bus.width;
|
||||
SIMUL_Printf(processor, "MEM Write *0x%x - 0x%x\n", cbs->x.bus.address, data);
|
||||
simulWord width = cbs->x.bus.width;
|
||||
SIMUL_Printf(processor, "MEM Write *0x%x - 0x%x\n", address, data);
|
||||
|
||||
simulWord writeAccess = 1;
|
||||
simulWord iadr = memlog->infoBase;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &address);
|
||||
iadr += 4;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &data);
|
||||
iadr += 4;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &width);
|
||||
iadr += 4;
|
||||
SIMUL_WriteMemory(processor, memlog->bustype, &iadr, 4, SIMUL_MEMORY_HIDDEN, &writeAccess);
|
||||
|
||||
|
||||
return SIMUL_MEMORY_OK;
|
||||
}
|
||||
|
||||
@ -66,6 +88,7 @@ int SIMULAPI SIMUL_Init(simulProcessor processor, simulCallbackStruct * cbs)
|
||||
// return SIMUL_INIT_FAIL;
|
||||
// }
|
||||
pmemlog->bustype = cbs->x.init.argpbustype[1];
|
||||
pmemlog->infoBase = 0x60000000; // placed at "external RAM"
|
||||
|
||||
simulWord from, to;
|
||||
from = 0x20002074;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
set(SRCS
|
||||
main.cc
|
||||
T32Connector.cc
|
||||
T32TraceFormat.cc
|
||||
T32Tracer.cc
|
||||
)
|
||||
|
||||
add_executable(fail-client ${SRCS})
|
||||
@ -10,3 +12,4 @@ install(TARGETS fail-client RUNTIME DESTINATION bin)
|
||||
|
||||
add_executable(t32cli t32cli.cc)
|
||||
target_link_libraries(t32cli t32api)
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@ int T32Connector::getState() const {
|
||||
|
||||
void T32Connector::showMemoryRegions(const memory_map_t& map) const {
|
||||
|
||||
for(int i = 0; i < map.size(); i++){
|
||||
for(unsigned int i = 0; i < map.size(); i++){
|
||||
std::cout << "[" << i << "] 0x" << std::hex << map[i].first << " - 0x" << std::hex << map[i].second << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
25
debuggers/t32/src/T32TraceFormat.cc
Normal file
25
debuggers/t32/src/T32TraceFormat.cc
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
#include "T32TraceFormat.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fail {
|
||||
|
||||
std::ostream& operator <<(std::ostream & os, const fail::T32TraceRecord & r) {
|
||||
#ifndef __puma
|
||||
if(r.isDataWrite()){
|
||||
os << "WRITE";
|
||||
} else if (r.isDataRead()) {
|
||||
os << "READ";
|
||||
} else if( r.isProgram()) {
|
||||
os << "PROGRAM";
|
||||
} else {
|
||||
os << "UNKNOWN";
|
||||
}
|
||||
os << "\t" << hex << (int)(r.getAddress()) << "\t" << r.getData() << "\t";
|
||||
#endif
|
||||
return os;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
66
debuggers/t32/src/T32Tracer.cc
Normal file
66
debuggers/t32/src/T32Tracer.cc
Normal file
@ -0,0 +1,66 @@
|
||||
#include "T32Tracer.hpp"
|
||||
#include "T32TraceFormat.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "t32.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fail {
|
||||
|
||||
T32Tracer::T32Tracer( const std::string& tfile ) : path(tfile), m_log("T32Tracer", false) {
|
||||
// TODO Check if tracing is available:
|
||||
avail = true;
|
||||
exportcmd = "Trace.export " + path;
|
||||
m_log << "Trace file: " << path.c_str() << endl;
|
||||
}
|
||||
|
||||
|
||||
void T32Tracer::setup() const {
|
||||
T32_Cmd("Trace.SIZE 16."); // trace 16 records
|
||||
T32_Cmd("Trace.METHOD Analyzer");
|
||||
T32_Cmd("Trace.autoarm on");
|
||||
T32_Cmd("Trace.autoinit on");
|
||||
T32_Cmd("Trace.state");
|
||||
}
|
||||
|
||||
int T32Tracer::evaluate(){
|
||||
// clear old records
|
||||
rvec.clear();
|
||||
|
||||
// export trace to file
|
||||
//T32_Cmd(static_cast<char*>(exportcmd.c_str()));
|
||||
T32_Cmd((char*)(exportcmd.c_str()));
|
||||
// open trace file.
|
||||
ifstream tracefile;
|
||||
tracefile.open(path.c_str(), ios::in | ios::binary);
|
||||
|
||||
if(tracefile.is_open()){
|
||||
// evaluate trace.
|
||||
T32TraceHeader hdr;
|
||||
tracefile.seekg(0, ios::beg);
|
||||
tracefile.read(reinterpret_cast<char*>(&hdr), sizeof(T32TraceHeader));
|
||||
|
||||
T32TraceRecord r;
|
||||
for(size_t i = 0; i < hdr.number_of_records; ++i) {
|
||||
tracefile.read(reinterpret_cast<char*>(&r), sizeof(T32TraceRecord));
|
||||
rvec.push_back(r); // add trace record to vector
|
||||
}
|
||||
return rvec.size();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool T32Tracer::wasDataAccess(void) const {
|
||||
// TODO if rvec.size() != 0 !!
|
||||
return rvec.back().isDataAccess();
|
||||
}
|
||||
|
||||
|
||||
void T32Tracer::dump(void){
|
||||
for(size_t i = 0; i < rvec.size(); ++i) {
|
||||
m_log << "[" << dec << i << "] " << rvec[i] << endl;
|
||||
}
|
||||
}
|
||||
|
||||
}; // end of namespace fail
|
||||
@ -22,6 +22,7 @@
|
||||
#include "util/optionparser/optionparser_ext.hpp"
|
||||
|
||||
#include "T32Connector.hpp"
|
||||
#include "T32Tracer.hpp"
|
||||
#include "t32config.hpp"
|
||||
#include "sal/MemoryInstruction.hpp"
|
||||
#include "util/Disassembler.hpp"
|
||||
@ -54,7 +55,6 @@ int main(int argc, char** argv){
|
||||
option::Option options[stats.options_max], buffer[stats.buffer_max];
|
||||
option::Parser parse(usage, argc, argv, options, buffer);
|
||||
|
||||
return 0;
|
||||
if (parse.error()){
|
||||
cerr << "Error parsing arguments." << endl;
|
||||
return 1;
|
||||
@ -101,18 +101,27 @@ int main(int argc, char** argv){
|
||||
MemoryInstruction mem;
|
||||
address_t ip;
|
||||
|
||||
// Enable T32 Tracer (if available)
|
||||
T32Tracer tr("/tmp/tr.x"); // TODO configurable trace file path
|
||||
tr.setup(); // enable and configure tracing.
|
||||
|
||||
while(1) {
|
||||
// Start execution (with next timeout, if any)
|
||||
t32.go();
|
||||
// Wait for debugger to stop.
|
||||
while( t32.isRunning() ) {}
|
||||
// Evaluate state.
|
||||
t32.test();// TODO
|
||||
|
||||
// Call appropriate callback of the SimulatorController.
|
||||
ip = fail::simulator.getCPU(0).getInstructionPointer();
|
||||
fail::simulator.onBreakpoint(&fail::simulator.getCPU(0), ip , fail::ANY_ADDR);
|
||||
if( meminstruction.eval(ip, mem) ) {
|
||||
fail::simulator.onMemoryAccess(&fail::simulator.getCPU(0), mem.getAddress(), mem.getWidth(), mem.isWriteAccess(), ip );
|
||||
|
||||
// Evaluate tracing result, handle memory access event..
|
||||
if((tr.evaluate() > 0) && (tr.wasDataAccess())){
|
||||
// TODO: step back in trace and find program counter of the according instruction.
|
||||
// ip = XXX;
|
||||
fail::simulator.onMemoryAccess(&fail::simulator.getCPU(0), tr.getLatestRecord().getAddress(), /* TODO access width: */ 4, tr.getLatestRecord().isDataWrite(), ip );
|
||||
|
||||
tr.dump();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user