blob: 0303ff6bf1dbce5dea28072d087dc829f69130be [file] [log] [blame]
/*
* Copyright (c) 2010-2011, 2014, 2016-2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "arch/arm/nativetrace.hh"
#include "arch/arm/regs/cc.hh"
#include "arch/arm/regs/misc.hh"
#include "base/compiler.hh"
#include "cpu/thread_context.hh"
#include "debug/ExecRegDelta.hh"
#include "params/ArmNativeTrace.hh"
#include "sim/byteswap.hh"
namespace gem5
{
using namespace ArmISA;
namespace Trace {
[[maybe_unused]] static const char *regNames[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "fp", "r12", "sp", "lr", "pc",
"cpsr", "f0", "f1", "f2", "f3", "f4", "f5", "f6",
"f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14",
"f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22",
"f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30",
"f31", "fpscr"
};
void
Trace::ArmNativeTrace::ThreadState::update(NativeTrace *parent)
{
oldState = state[current];
current = (current + 1) % 2;
newState = state[current];
memcpy(newState, oldState, sizeof(state[0]));
uint64_t diffVector;
parent->read(&diffVector, sizeof(diffVector));
diffVector = letoh(diffVector);
int changes = 0;
for (int i = 0; i < STATE_NUMVALS; i++) {
if (diffVector & 0x1) {
changed[i] = true;
changes++;
} else {
changed[i] = false;
}
diffVector >>= 1;
}
uint64_t values[changes];
parent->read(values, sizeof(values));
int pos = 0;
for (int i = 0; i < STATE_NUMVALS; i++) {
if (changed[i]) {
newState[i] = letoh(values[pos++]);
changed[i] = (newState[i] != oldState[i]);
}
}
}
void
Trace::ArmNativeTrace::ThreadState::update(ThreadContext *tc)
{
oldState = state[current];
current = (current + 1) % 2;
newState = state[current];
// Regular int regs
for (int i = 0; i < 15; i++) {
newState[i] = tc->getReg(RegId(IntRegClass, i));
changed[i] = (oldState[i] != newState[i]);
}
//R15, aliased with the PC
newState[STATE_PC] = tc->pcState().as<ArmISA::PCState>().npc();
changed[STATE_PC] = (newState[STATE_PC] != oldState[STATE_PC]);
//CPSR
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
cpsr.nz = tc->getReg(cc_reg::Nz);
cpsr.c = tc->getReg(cc_reg::C);
cpsr.v = tc->getReg(cc_reg::V);
cpsr.ge = tc->getReg(cc_reg::Ge);
newState[STATE_CPSR] = cpsr;
changed[STATE_CPSR] = (newState[STATE_CPSR] != oldState[STATE_CPSR]);
for (int i = 0; i < NumVecV7ArchRegs; i++) {
ArmISA::VecRegContainer vec_container;
tc->getReg(RegId(VecRegClass, i), &vec_container);
auto *vec = vec_container.as<uint64_t>();
newState[STATE_F0 + 2*i] = vec[0];
newState[STATE_F0 + 2*i + 1] = vec[1];
}
newState[STATE_FPSCR] = tc->readMiscRegNoEffect(MISCREG_FPSCR) |
tc->getReg(cc_reg::Fp);
}
void
Trace::ArmNativeTrace::check(NativeTraceRecord *record)
{
ThreadContext *tc = record->getThread();
// This area is read only on the target. It can't stop there to tell us
// what's going on, so we should skip over anything there also.
if (tc->pcState().as<ArmISA::PCState>().npc() > 0xffff0000)
return;
nState.update(this);
mState.update(tc);
// If a syscall just happened native trace needs another tick
if ((mState.oldState[STATE_PC] == nState.oldState[STATE_PC]) &&
(mState.newState[STATE_PC] - 4 == nState.newState[STATE_PC])) {
DPRINTF(ExecRegDelta, "Advancing to match PCs after syscall\n");
nState.update(this);
}
bool errorFound = false;
// Regular int regs
for (int i = 0; i < STATE_NUMVALS; i++) {
if (nState.changed[i] || mState.changed[i]) {
bool oldMatch = (mState.oldState[i] == nState.oldState[i]);
bool newMatch = (mState.newState[i] == nState.newState[i]);
if (oldMatch && newMatch) {
// The more things change, the more they stay the same.
continue;
}
errorFound = true;
#ifndef NDEBUG
const char *vergence = " ";
if (oldMatch && !newMatch) {
vergence = "<>";
} else if (!oldMatch && newMatch) {
vergence = "><";
}
if (!nState.changed[i]) {
DPRINTF(ExecRegDelta, "%s [%5s] "\
"Native: %#010x "\
"M5: %#010x => %#010x\n",
vergence, regNames[i],
nState.newState[i],
mState.oldState[i], mState.newState[i]);
} else if (!mState.changed[i]) {
DPRINTF(ExecRegDelta, "%s [%5s] "\
"Native: %#010x => %#010x "\
"M5: %#010x \n",
vergence, regNames[i],
nState.oldState[i], nState.newState[i],
mState.newState[i]);
} else {
DPRINTF(ExecRegDelta, "%s [%5s] "\
"Native: %#010x => %#010x "\
"M5: %#010x => %#010x\n",
vergence, regNames[i],
nState.oldState[i], nState.newState[i],
mState.oldState[i], mState.newState[i]);
}
#endif
}
}
if (errorFound) {
StaticInstPtr inst = record->getStaticInst();
assert(inst);
bool ran = true;
if (inst->isMicroop()) {
ran = false;
inst = record->getMacroStaticInst();
}
assert(inst);
record->traceInst(inst, ran);
bool pcError = (mState.newState[STATE_PC] !=
nState.newState[STATE_PC]);
if (stopOnPCError && pcError)
panic("Native trace detected an error in control flow!");
}
}
} // namespace Trace
} // namespace gem5