From 7d951b7697d0c98cb900c0c4f8e9ec0785bfbb52 Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Mon, 5 Oct 2015 23:32:25 +0200 Subject: [PATCH] rend/if: Logging/verification of MD5(ta command list data) This allows for some very primitive auto-automated testing, by comparing known good frames. As this happens on the TA level, it doesn't actually require rendering (and thus it's server friendly) Two new config entries under the new [testing] namespace control behavior - ta.HashLogFile, file where data should be logged, empty if not logging - ta.HashCheckFile, past log to check against, empty if not checking The emu will crash via verify if the logs don't match, and exit(1) if they do --- core/hw/pvr/Renderer_if.cpp | 60 +++++++++++++++++++++++++++++++++++++ core/nullDC.cpp | 2 ++ core/types.h | 3 ++ 3 files changed, 65 insertions(+) diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index e84ad1103..ad1a75a44 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -5,10 +5,16 @@ #include "deps/zlib/zlib.h" +#include "deps/crypto/md5.h" + #if FEAT_HAS_NIXPROF #include "profiler/profiler.h" #endif +#define FRAME_MD5 0x1 +FILE* fLogFrames; +FILE* fCheckFrames; + /* rendv3 ideas @@ -291,6 +297,48 @@ void rend_start_render() if (ctx) { + if (fLogFrames || fCheckFrames) { + MD5Context md5; + u8 digest[16]; + + MD5Init(&md5); + MD5Update(&md5, ctx->tad.thd_root, ctx->tad.End() - ctx->tad.thd_root); + MD5Final(digest, &md5); + + if (fLogFrames) { + fputc(FRAME_MD5, fLogFrames); + fwrite(digest, 1, 16, fLogFrames); + fflush(fLogFrames); + } + + if (fCheckFrames) { + u8 v; + u8 digest2[16]; + int ch = fgetc(fCheckFrames); + + if (ch == EOF) { + printf("Testing: TA Hash log matches, exiting\n"); + exit(1); + } + + verify(ch == FRAME_MD5); + + fread(digest2, 1, 16, fCheckFrames); + + verify(memcmp(digest, digest2, 16) == 0); + + + } + + /* + u8* dig = digest; + printf("FRAME: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", + digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], + digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15] + ); + */ + } + if (!ctx->rend.Overrun) { //printf("REP: %.2f ms\n",render_end_pending_cycles/200000.0); @@ -366,6 +414,13 @@ void rend_end_wait() bool rend_init() { + if (fLogFrames = fopen(settings.pvr.HashLogFile.c_str(), "wb")) { + printf("Saving frame hashes to: '%s'\n", settings.pvr.HashLogFile.c_str()); + } + + if (fCheckFrames = fopen(settings.pvr.HashCheckFile.c_str(), "rb")) { + printf("Comparing frame hashes against: '%s'\n", settings.pvr.HashCheckFile.c_str()); + } #ifdef NO_REND renderer = rend_norend(); @@ -435,6 +490,11 @@ bool rend_init() void rend_term() { + if (fCheckFrames) + fclose(fCheckFrames); + + if (fLogFrames) + fclose(fLogFrames); } void rend_vblank() diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 058aadbd2..0358c6828 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -260,6 +260,8 @@ void LoadSettings() #endif settings.bios.UseReios = cfgLoadInt("config", "bios.UseReios", 0); + settings.pvr.HashLogFile = cfgLoadStr("testing", "ta.HashLogFile", ""); + settings.pvr.HashCheckFile = cfgLoadStr("testing", "ta.HashCheckFile", ""); #if (HOST_OS != OS_LINUX || defined(_ANDROID) || defined(TARGET_PANDORA)) settings.aica.BufferSize=2048; diff --git a/core/types.h b/core/types.h index 7e60fbe13..b4421dfce 100644 --- a/core/types.h +++ b/core/types.h @@ -696,6 +696,9 @@ struct settings_t u32 MaxThreads; u32 SynchronousRendering; + + string HashLogFile; + string HashCheckFile; } pvr; struct {