From e9e358cdd0930d50d1bc62268397c9d32c33116d Mon Sep 17 00:00:00 2001 From: bbracker Date: Fri, 25 Feb 2022 23:51:40 +0000 Subject: [PATCH] revived checkpointing and hacked it up to generate a trace starting at the checkpoint --- .gitignore | 4 + linux/testvector-generation/Makefile | 17 ++++ .../createGenCheckpointScript.py | 71 +++++++++++++ linux/testvector-generation/fixBinMem.c | 33 +++++++ linux/testvector-generation/genCheckpoint.sh | 89 +++++++++++++++++ linux/testvector-generation/parseState.py | 99 +++++++++++++++++++ linux/testvector-generation/silencePipe.c | 19 ++++ 7 files changed, 332 insertions(+) create mode 100644 linux/testvector-generation/Makefile create mode 100755 linux/testvector-generation/createGenCheckpointScript.py create mode 100644 linux/testvector-generation/fixBinMem.c create mode 100755 linux/testvector-generation/genCheckpoint.sh create mode 100755 linux/testvector-generation/parseState.py create mode 100644 linux/testvector-generation/silencePipe.c diff --git a/.gitignore b/.gitignore index 368992380..9b3e6dc62 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,10 @@ examples/C/sum/sum examples/C/fir/fir linux/devicetree/debug/* !linux/devicetree/debug/dump-dts.sh +linux/testvector-generation/genCheckpoint.gdb +linux/testvector-generation/silencePipe +linux/testvector-generation/silencePipe.control +linux/testvector-generation/fixBinMem *.dtb synthDC/WORK synthDC/alib-52 diff --git a/linux/testvector-generation/Makefile b/linux/testvector-generation/Makefile new file mode 100644 index 000000000..8f3441e5a --- /dev/null +++ b/linux/testvector-generation/Makefile @@ -0,0 +1,17 @@ +SHELL = /bin/sh + +CFLAG = -Wall -g +CC = clang + +all: fixBinMem silencePipe + +fixBinMem: fixBinMem.c + ${CC} ${CFLAGS} fixBinMem.c -o fixBinMem + chmod +x fixBinMem + +silencePipe: silencePipe.c + ${CC} ${CFLAGS} silencePipe.c -o silencePipe + chmod +x silencePipe +clean: + -rm -f fixBinMem + -rm -f silencePipe diff --git a/linux/testvector-generation/createGenCheckpointScript.py b/linux/testvector-generation/createGenCheckpointScript.py new file mode 100755 index 000000000..fee2b603a --- /dev/null +++ b/linux/testvector-generation/createGenCheckpointScript.py @@ -0,0 +1,71 @@ +#! /usr/bin/python3 +import sys + +if len(sys.argv) != 8: + sys.exit("""Error createGenCheckpointScript.py expects 7 args: + + + + + + + """) + +tcpPort=sys.argv[1] +vmlinux=sys.argv[2] +instrCount=sys.argv[3] +statePath=sys.argv[4] +ramPath=sys.argv[5] +checkPC=sys.argv[6] +checkPCoccurences=sys.argv[7] + +GDBscript = f""" +# GDB config +set pagination off +set logging overwrite on +set logging redirect on +set confirm off + +# Connect to QEMU session +target extended-remote :{tcpPort} + +# QEMU Config +maintenance packet Qqemu.PhyMemMode:1 + +# Symbol file +file {vmlinux} + +# Silence Trace Generation +shell echo 1 > ./silencePipe.control + +# Step over reset vector into actual code +stepi 100 +# Proceed to checkpoint +print "GDB proceeding to checkpoint at {instrCount} instrs, pc {checkPC}\\n" +b *0x{checkPC} +ignore 1 {checkPCoccurences} +c +print "Reached checkpoint at {instrCount} instrs\\n" + +# Log all registers to a file +printf "GDB storing state to {statePath}\\n" +set logging file {statePath} +set logging on +info all-registers +set logging off + +# Log main memory to a file +print "GDB storing RAM to {ramPath}\\n" +#dump binary memory {ramPath} 0x80000000 0xffffffff +#dump binary memory {ramPath} 0x80000000 0x80ffffff + +# Generate Trace Until End +shell echo 0 > ./silencePipe.control +# Do this by setting an impossible breakpoint +b *0x1000 +del 1 +c +""" +GDBscriptFile = open("genCheckpoint.gdb",'w') +GDBscriptFile.write(GDBscript) +GDBscriptFile.close() diff --git a/linux/testvector-generation/fixBinMem.c b/linux/testvector-generation/fixBinMem.c new file mode 100644 index 000000000..fe071008b --- /dev/null +++ b/linux/testvector-generation/fixBinMem.c @@ -0,0 +1,33 @@ +#include +#include +#include +int main(int argc, char *argv[]) { + if (argc < 3){ + fprintf(stderr, "Expected 2 arguments: \n"); + exit(1); + } + char* rawGDBfilePath = argv[1]; + FILE* rawGDBfile; + if ((rawGDBfile = fopen(rawGDBfilePath,"rb"))==NULL) { + fprintf(stderr, "File not found: %s\n",rawGDBfilePath); + exit(1); + } + char* outFilePath = argv[2]; + FILE* outFile = fopen(outFilePath,"w"); + uint64_t qemuWord; + uint64_t verilogWord; + int bytesReturned=0; + do { + bytesReturned=fread(&qemuWord, 8, 1, rawGDBfile); + verilogWord = (((qemuWord>>0 )&0xff)<<56 | + ((qemuWord>>8 )&0xff)<<48 | + ((qemuWord>>16)&0xff)<<40 | + ((qemuWord>>24)&0xff)<<32 | + ((qemuWord>>32)&0xff)<<24 | + ((qemuWord>>40)&0xff)<<16 | + ((qemuWord>>48)&0xff)<<8 | + ((qemuWord>>56)&0xff)<<0); + fwrite(&verilogWord, 8, 1, outFile); + } while(bytesReturned!=0); + return 0; +} diff --git a/linux/testvector-generation/genCheckpoint.sh b/linux/testvector-generation/genCheckpoint.sh new file mode 100755 index 000000000..d468e9fda --- /dev/null +++ b/linux/testvector-generation/genCheckpoint.sh @@ -0,0 +1,89 @@ +#!/bin/bash +tcpPort=1237 +imageDir=$RISCV/buildroot/output/images +tvDir=$RISCV/linux-testvectors +recordFile="$tvDir/all.qemu" +traceFile="$tvDir/all_up_to_498.txt" + +# Parse Commandline Arg +if [ "$#" -ne 1 ]; then + echo "genCheckpoint requires 1 argument: " >&2 + exit 1 +fi +instrs=$1 +if ! [ "$instrs" -eq "$instrs" ] 2> /dev/null +then + echo "Error expected integer number of instructions, got $instrs" >&2 + exit 1 +fi + +checkPtDir="$tvDir/checkpoint$instrs" +outTraceFile="$checkPtDir/all.txt" +interruptsFile="$checkPtDir/interrupts.txt" +rawStateFile="$checkPtDir/stateGDB.txt" +rawRamFile="$checkPtDir/ramGDB.bin" +ramFile="$checkPtDir/ram.bin" + +read -p "This scripts is going to create a checkpoint at $instrs instrs. +Is that what you wanted? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]] +then + echo "Creating checkpoint at $instrs instructions!" + make silencePipe + + # Create Output Directory + echo "Elevating permissions to create $checkPtDir and stuff inside it" + sudo mkdir -p $checkPtDir + sudo chown -R cad:users $checkPtDir + sudo chmod -R a+rw $checkPtDir + sudo touch $outTraceFile + sudo chmod a+rw $outTraceFile + sudo touch $interruptsFile + sudo chmod a+rw $interruptsFile + sudo touch $rawStateFile + sudo chmod a+rw $rawStateFile + sudo touch $rawRamFile + sudo chmod a+rw $rawRamFile + sudo touch $ramFile + sudo chmod a+rw $ramFile + + # Identify instruction in trace + instr=$(sed "${instrs}q;d" "$traceFile") + echo "Found ${instrs}th instr: ${instr}" + pc=$(echo $instr | cut -d " " -f1) + asm=$(echo $instr | cut -d " " -f2) + occurences=$(($(head -$instrs "$traceFile" | grep -c "${pc} ${asm}")-1)) + echo "It occurs ${occurences} times before the ${instrs}th instr." + + # Create GDB script because GDB is terrible at handling arguments / variables + ./createGenCheckpointScript.py $tcpPort $imageDir/vmlinux $instrs $rawStateFile $rawRamFile $pc $occurences + + # GDB+QEMU + echo "Starting QEMU in replay mode with attached GDB script at $(date +%H:%M:%S)" + (qemu-system-riscv64 \ + -M virt -dtb $imageDir/wally-virt.dtb \ + -nographic \ + -bios $imageDir/fw_jump.elf -kernel $imageDir/Image -append "root=/dev/vda ro" -initrd $imageDir/rootfs.cpio \ + -singlestep -rtc clock=vm -icount shift=0,align=off,sleep=on,rr=replay,rrfile=$recordFile \ + -d nochain,cpu,in_asm,int \ + -gdb tcp::$tcpPort -S \ + 2>&1 >/dev/null | ./silencePipe | ./parseQEMUtoGDB/parseQEMUtoGDB_run.py | ./parseGDBtoTrace/parseGDBtoTrace_run.py $interruptsFile | ./remove_dup.awk > $outTraceFile) \ + & riscv64-unknown-elf-gdb --quiet -ex "source genCheckpoint.gdb" + echo "Completed GDB script at $(date +%H:%M:%S)" + + # Post-Process GDB outputs + ./parseState.py "$checkPtDir" + echo "Changing Endianness at $(date +%H:%M:%S)" + make fixBinMem + ./fixBinMem "$rawRamFile" "$ramFile" + #echo "Creating truncated trace at $(date +%H:%M:%S)" + #tail -n+$instrs "$tvDir/$traceFile" > "$checkPtDir/$traceFile" + echo "Checkpoint completed at $(date +%H:%M:%S)" + + # Cleanup + echo "Elevating permissions to restrict write access to $checkPtDir" + sudo chown -R cad:users $checkPtDir + sudo chmod -R go-w $checkPtDir +fi + diff --git a/linux/testvector-generation/parseState.py b/linux/testvector-generation/parseState.py new file mode 100755 index 000000000..5275597c4 --- /dev/null +++ b/linux/testvector-generation/parseState.py @@ -0,0 +1,99 @@ +#! /usr/bin/python3 +import sys, os + +################ +# Helper Funcs # +################ + +def tokenize(string): + tokens = [] + token = '' + whitespace = 0 + prevWhitespace = 0 + for char in string: + prevWhitespace = whitespace + whitespace = char in ' \t\n' + if (whitespace): + if ((not prevWhitespace) and (token != '')): + tokens.append(token) + token = '' + else: + token = token + char + return tokens + +############# +# Main Code # +############# +print("Begin parsing state.") + +# Parse Args +if len(sys.argv) != 2: + sys.exit('Error parseState.py expects 1 arg:\n parseState.py ') +outDir = sys.argv[1]+'/' +stateGDBpath = outDir+'stateGDB.txt' +if not os.path.exists(stateGDBpath): + sys.exit('Error input file '+stateGDBpath+'not found') + +singleCSRs = ['pc','mip','mie','mscratch','mcause','mepc','mtvec','medeleg','mideleg','sscratch','scause','sepc','stvec','sedeleg','sideleg','satp','mstatus','priv'] +# priv (current privilege mode) isn't technically a CSR but we can log it with the same machinery +thirtyTwoBitCSRs = ['mcounteren','scounteren'] +listCSRs = ['hpmcounter','pmpaddr'] +pmpcfg = ['pmpcfg'] + +# Initialize List CSR files to empty +# (because later we'll open them in append mode) +for csr in listCSRs+pmpcfg: + outFileName = 'checkpoint-'+csr.upper() + outFile = open(outDir+outFileName, 'w') + outFile.close() + +# Initial State for Main Loop +currState = 'regFile' +regFileIndex = 0 +outFileName = 'checkpoint-RF' +outFile = open(outDir+outFileName, 'w') + +# Main Loop +with open(stateGDBpath, 'r') as stateGDB: + for line in stateGDB: + line = tokenize(line) + name = line[0] + val = line[1][2:] + if (currState == 'regFile'): + if (regFileIndex == 0 and name != 'zero'): + print('Whoops! Expected regFile registers to come first, starting with zero') + exit(1) + if (name != 'zero'): + # Wally doesn't need to know zero=0 + outFile.write(val+'\n') + regFileIndex += 1 + if (regFileIndex == 32): + outFile.close() + currState = 'CSRs' + elif (currState == 'CSRs'): + if name in singleCSRs: + outFileName = 'checkpoint-'+name.upper() + outFile = open(outDir+outFileName, 'w') + outFile.write(val+'\n') + outFile.close() + elif name in thirtyTwoBitCSRs: + outFileName = 'checkpoint-'+name.upper() + outFile = open(outDir+outFileName, 'w') + val = int(val,16) & 0xffffffff + outFile.write(hex(val)[2:]+'\n') + outFile.close() + elif name.strip('0123456789') in listCSRs: + outFileName = 'checkpoint-'+name.upper().strip('0123456789') + outFile = open(outDir+outFileName, 'a') + outFile.write(val+'\n') + outFile.close() + elif name.strip('0123456789') in pmpcfg: + outFileName = 'checkpoint-'+name.upper().strip('0123456789') + outFile = open(outDir+outFileName, 'a') + fourPmp = int(val,16) + for i in range(0,4): + byte = (fourPmp >> 8*i) & 0xff + outFile.write(hex(byte)[2:]+'\n') + outFile.close() + +print("Finished parsing state!") diff --git a/linux/testvector-generation/silencePipe.c b/linux/testvector-generation/silencePipe.c new file mode 100644 index 000000000..2cd531b89 --- /dev/null +++ b/linux/testvector-generation/silencePipe.c @@ -0,0 +1,19 @@ +#include +#include + +int main(void) +{ + char *line = NULL; + size_t len = 0; + while (1) { + FILE* controlFile = fopen("silencePipe.control", "r"); + char silenceChar = getc(controlFile); + fclose(controlFile); + ssize_t lineSize = getline(&line, &len, stdin); + if (silenceChar!='1') { + printf("%s",line); + } + } + free(line); + return 0; +}