1/* 2 * Copyright (c) 2017-2018 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Giacomo Travaglini 38 */ 39 40#include "arch/arm/tracers/tarmac_record.hh" 41 42#include "arch/arm/insts/static_inst.hh" 43#include "tarmac_tracer.hh" 44 45namespace Trace { 46 47// TARMAC Instruction Record static variables 48uint64_t TarmacTracerRecord::TraceInstEntry::instCount = 0; 49 50std::string 51iSetStateToStr(TarmacBaseRecord::ISetState isetstate) 52{ 53 switch (isetstate) { 54 case TarmacBaseRecord::ISET_ARM: 55 return "A"; 56 case TarmacBaseRecord::ISET_THUMB: 57 return "T"; 58 case TarmacBaseRecord::ISET_A64: 59 return "O"; 60 default: 61 return "Unsupported"; 62 } 63} 64 65std::string 66opModeToStr(OperatingMode opMode) 67{ 68 switch (opMode) { 69 case MODE_EL0T: 70 return "EL0t"; 71 case MODE_EL1T: 72 return "EL1t"; 73 case MODE_EL1H: 74 return "EL1h"; 75 case MODE_EL2T: 76 return "EL2t"; 77 case MODE_EL2H: 78 return "EL2h"; 79 case MODE_EL3T: 80 return "EL3t"; 81 case MODE_EL3H: 82 return "EL3h"; 83 case MODE_USER: 84 return "usr"; 85 case MODE_FIQ: 86 return "fiq"; 87 case MODE_IRQ: 88 return "irq"; 89 case MODE_SVC: 90 return "svc"; 91 case MODE_MON: 92 return "mon"; 93 case MODE_ABORT: 94 return "abt"; 95 case MODE_HYP: 96 return "hyp"; 97 case MODE_UNDEFINED: 98 return "und"; 99 case MODE_SYSTEM: 100 return "sys"; 101 default: 102 return "Unsupported"; 103 } 104} 105 106// TarmacTracerRecord ctor 107TarmacTracerRecord::TarmacTracerRecord(Tick _when, ThreadContext *_thread, 108 const StaticInstPtr _staticInst, 109 PCState _pc, 110 TarmacTracer& _tracer, 111 const StaticInstPtr _macroStaticInst) 112 : TarmacBaseRecord(_when, _thread, _staticInst, 113 _pc, _macroStaticInst), 114 tracer(_tracer) 115{ 116} 117 118TarmacTracerRecord::TraceInstEntry::TraceInstEntry( 119 const TarmacContext& tarmCtx, 120 bool predicate) 121 : InstEntry(tarmCtx.thread, tarmCtx.pc, tarmCtx.staticInst, predicate) 122{ 123 secureMode = inSecureState(tarmCtx.thread); 124 125 auto arm_inst = static_cast<const ArmStaticInst*>( 126 tarmCtx.staticInst.get() 127 ); 128 129 // Get the instruction size as a number of bits: 130 // (multiply byte size by 8) 131 instSize = (arm_inst->instSize() << 3); 132 133 // Mask the opcode using the instruction size: the 134 // opcode field will otherwise be 32 bit wide even 135 // for 16bit (Thumb) instruction. 136 opcode = arm_inst->encoding(); 137 138 // Update the instruction count: number of executed 139 // instructions. 140 instCount++; 141} 142 143TarmacTracerRecord::TraceMemEntry::TraceMemEntry( 144 const TarmacContext& tarmCtx, 145 uint8_t _size, Addr _addr, uint64_t _data) 146 : MemEntry(_size, _addr, _data), 147 loadAccess(tarmCtx.staticInst->isLoad()) 148{ 149} 150 151TarmacTracerRecord::TraceRegEntry::TraceRegEntry( 152 const TarmacContext& tarmCtx, 153 const RegId& reg) 154 : RegEntry(tarmCtx.pc), 155 regValid(false), 156 regClass(reg.classValue()), 157 regRel(reg.index()) 158{ 159} 160 161void 162TarmacTracerRecord::TraceRegEntry::update( 163 const TarmacContext& tarmCtx 164) 165{ 166 // Fill the register entry data, according to register 167 // class. 168 switch (regClass) { 169 case CCRegClass: 170 updateCC(tarmCtx, regRel); 171 break; 172 case FloatRegClass: 173 updateFloat(tarmCtx, regRel); 174 break; 175 case IntRegClass: 176 updateInt(tarmCtx, regRel); 177 break; 178 case MiscRegClass: 179 updateMisc(tarmCtx, regRel); 180 break; 181 default: 182 // If unsupported format, do nothing: non updating 183 // the register will prevent it to be printed. 184 break; 185 } 186} 187 188void 189TarmacTracerRecord::TraceRegEntry::updateMisc( 190 const TarmacContext& tarmCtx, 191 RegIndex regRelIdx 192) 193{ 194 auto thread = tarmCtx.thread; 195 196 regValid = true; 197 regName = miscRegName[regRelIdx]; 198 valueLo = thread->readMiscRegNoEffect(regRelIdx); 199 200 // If it is the CPSR: 201 // update the value of the CPSR register and add 202 // the CC flags on top of the value 203 if (regRelIdx == MISCREG_CPSR) { 204 CPSR cpsr = thread->readMiscRegNoEffect(MISCREG_CPSR); 205 cpsr.nz = thread->readCCReg(CCREG_NZ); 206 cpsr.c = thread->readCCReg(CCREG_C); 207 cpsr.v = thread->readCCReg(CCREG_V); 208 cpsr.ge = thread->readCCReg(CCREG_GE); 209 210 // update the entry value 211 valueLo = cpsr; 212 } 213} 214 215void 216TarmacTracerRecord::TraceRegEntry::updateCC( 217 const TarmacContext& tarmCtx, 218 RegIndex regRelIdx 219) 220{ 221 auto thread = tarmCtx.thread; 222 223 regValid = true; 224 regName = ccRegName[regRelIdx]; 225 valueLo = thread->readCCReg(regRelIdx); 226} 227 228void 229TarmacTracerRecord::TraceRegEntry::updateFloat( 230 const TarmacContext& tarmCtx, 231 RegIndex regRelIdx 232) 233{ 234 auto thread = tarmCtx.thread; 235 236 regValid = true; 237 regName = "f" + std::to_string(regRelIdx); 238 valueLo = bitsToFloat32(thread->readFloatReg(regRelIdx)); 239} 240 241void 242TarmacTracerRecord::TraceRegEntry::updateInt( 243 const TarmacContext& tarmCtx, 244 RegIndex regRelIdx 245) 246{ 247 auto thread = tarmCtx.thread; 248 249 // Reading operating mode from CPSR. 250 // This is needed when printing the register name in case 251 // of banked register (e.g. lr_svc) 252 CPSR cpsr = thread->readMiscRegNoEffect(MISCREG_CPSR); 253 OperatingMode mode = (OperatingMode)(uint8_t)cpsr.mode; 254 255 std::string reg_suffix; 256 if (mode != MODE_USER) { 257 reg_suffix = "_" + opModeToStr(mode); 258 } 259 260 regValid = true; 261 switch (regRelIdx) { 262 case PCReg: 263 regName = "pc"; 264 break; 265 case StackPointerReg: 266 regName = "sp" + reg_suffix ; 267 break; 268 case FramePointerReg: 269 regName = "fp" + reg_suffix; 270 break; 271 case ReturnAddressReg: 272 regName = "lr" + reg_suffix; 273 break; 274 default: 275 regName = "r" + std::to_string(regRelIdx); 276 break; 277 } 278 valueLo = thread->readIntReg(regRelIdx); 279} 280 281void 282TarmacTracerRecord::addInstEntry(std::vector<InstPtr>& queue, 283 const TarmacContext& tarmCtx) 284{ 285 // Generate an instruction entry in the record and 286 // add it to the Instruction Queue 287 queue.push_back( 288 m5::make_unique<TraceInstEntry>(tarmCtx, predicate) 289 ); 290} 291 292void 293TarmacTracerRecord::addMemEntry(std::vector<MemPtr>& queue, 294 const TarmacContext& tarmCtx) 295{ 296 // Generate a memory entry in the record if the record 297 // implies a valid memory access, and add it to the 298 // Memory Queue 299 if (getMemValid()) { 300 queue.push_back( 301 m5::make_unique<TraceMemEntry>(tarmCtx, 302 static_cast<uint8_t>(getSize()), 303 getAddr(), getIntData()) 304 ); 305 } 306} 307 308void 309TarmacTracerRecord::addRegEntry(std::vector<RegPtr>& queue, 310 const TarmacContext& tarmCtx) 311{ 312 // Generate an entry for every ARM register being 313 // written by the current instruction 314 for (auto reg = 0; reg < staticInst->numDestRegs(); ++reg) { 315 316 RegId reg_id = staticInst->destRegIdx(reg); 317 318 // Creating a single register change entry 319 auto single_reg = genRegister<TraceRegEntry>(tarmCtx, reg_id); 320 321 // Copying the entry and adding it to the "list" 322 // of entries to be dumped to trace. 323 queue.push_back( 324 m5::make_unique<TraceRegEntry>(single_reg) 325 ); 326 } 327 328 // Gem5 is treating CPSR flags as separate registers (CC registers), 329 // in contrast with Tarmac specification: we need to merge the gem5 CC 330 // entries altogether with the CPSR register and produce a single entry. 331 mergeCCEntry<TraceRegEntry>(queue, tarmCtx); 332} 333 334void 335TarmacTracerRecord::dump() 336{ 337 // Generate and print all the record's entries. 338 auto &instQueue = tracer.instQueue; 339 auto &memQueue = tracer.memQueue; 340 auto ®Queue = tracer.regQueue; 341 342 const TarmacContext tarmCtx( 343 thread, 344 staticInst->isMicroop()? macroStaticInst : staticInst, 345 pc 346 ); 347 348 if (!staticInst->isMicroop()) { 349 // Current instruction is NOT a micro-instruction: 350 // Generate Tarmac entries and dump them immediately 351 352 // Generate Tarmac entries and add them to the respective 353 // queues. 354 addInstEntry(instQueue, tarmCtx); 355 addMemEntry(memQueue, tarmCtx); 356 addRegEntry(regQueue, tarmCtx); 357 358 // Flush (print) any queued entry. 359 flushQueues(instQueue, memQueue, regQueue); 360 361 } else { 362 // Current instruction is a micro-instruction: 363 // save micro entries into dedicated queues and flush them 364 // into the tracefile only when the MACRO-instruction 365 // has completed. 366 367 if (staticInst->isFirstMicroop()) { 368 addInstEntry(instQueue, tarmCtx); 369 } 370 371 addRegEntry(regQueue, tarmCtx); 372 addMemEntry(memQueue, tarmCtx); 373 374 if (staticInst->isLastMicroop()) { 375 // Flush (print) any queued entry. 376 flushQueues(instQueue, memQueue, regQueue); 377 } 378 } 379} 380 381template<typename Queue> 382void 383TarmacTracerRecord::flushQueues(Queue& queue) 384{ 385 std::ostream &outs = Trace::output(); 386 387 for (const auto &single_entry : queue) { 388 single_entry->print(outs); 389 } 390 391 queue.clear(); 392} 393 394template<typename Queue, typename... Args> 395void 396TarmacTracerRecord::flushQueues(Queue& queue, Args & ... args) 397{ 398 flushQueues(queue); 399 flushQueues(args...); 400} 401 402void 403TarmacTracerRecord::TraceInstEntry::print( 404 std::ostream& outs, 405 int verbosity, 406 const std::string &prefix) const 407{ 408 // Pad the opcode 409 std::string opcode_str = csprintf("%0*x", instSize >> 2, opcode); 410 411 // Print the instruction record formatted according 412 // to the Tarmac specification 413 ccprintf(outs, "%s clk %s (%u) %08x %s %s %s_%s : %s\n", 414 curTick(), /* Tick time */ 415 taken? "IT" : "IS", /* Instruction taken/skipped */ 416 instCount, /* Instruction count */ 417 addr, /* Instruction address */ 418 opcode_str, /* Instruction opcode */ 419 iSetStateToStr(isetstate), /* Instruction Set */ 420 opModeToStr(mode), /* Exception level */ 421 secureMode? "s" : "ns", /* Security */ 422 disassemble); /* Instruction disass */ 423} 424 425void 426TarmacTracerRecord::TraceMemEntry::print( 427 std::ostream& outs, 428 int verbosity, 429 const std::string &prefix) const 430{ 431 // Print the memory record formatted according 432 // to the Tarmac specification 433 ccprintf(outs, "%s clk M%s%d %08x %0*x\n", 434 curTick(), /* Tick time */ 435 loadAccess? "R" : "W", /* Access type */ 436 size, /* Access size */ 437 addr, /* Memory address */ 438 size*2, /* Padding with access size */ 439 data); /* Memory data */ 440} 441 442void 443TarmacTracerRecord::TraceRegEntry::print( 444 std::ostream& outs, 445 int verbosity, 446 const std::string &prefix) const 447{ 448 // Print the register record formatted according 449 // to the Tarmac specification 450 if (regValid) 451 ccprintf(outs, "%s clk R %s %08x\n", 452 curTick(), /* Tick time */ 453 regName, /* Register name */ 454 valueLo); /* Register value */ 455} 456 457} // namespace Trace 458