255 lines
6.0 KiB
C
255 lines
6.0 KiB
C
/*
|
|
** A utility program to decode tmstmpvfs log files.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
/*
|
|
** The six bytes at a[] are a big-endian unsigned integer which is the
|
|
** number of milliseconds since 1970. Decode that value into an ISO 8601
|
|
** date/time string stored in static space and return a pointer to that
|
|
** string.
|
|
*/
|
|
static const char *decodeTimestamp(const unsigned char *a){
|
|
uint64_t ms; /* Milliseconds since 1970 */
|
|
uint64_t days; /* Days since 1970-01-01 */
|
|
uint64_t sod; /* Start of date specified by ms */
|
|
uint64_t z; /* Days since 0000-03-01 */
|
|
uint64_t era; /* 400-year era */
|
|
int i; /* Loop counter */
|
|
int h; /* hour */
|
|
int m; /* minute */
|
|
int s; /* second */
|
|
int f; /* millisecond */
|
|
int Y; /* year */
|
|
int M; /* month */
|
|
int D; /* day */
|
|
int y; /* year assuming March is first month */
|
|
unsigned int doe; /* day of 400-year era */
|
|
unsigned int yoe; /* year of 400-year era */
|
|
unsigned int doy; /* day of year */
|
|
unsigned int mp; /* month with March==0 */
|
|
static char zOut[50]; /* Return results here */
|
|
|
|
for(ms=0, i=0; i<=5; i++) ms = (ms<<8) + a[i];
|
|
if( ms==0 ){
|
|
return " ";
|
|
}else if( ms>4102444800000LL ){ /* 2100-01-01 */
|
|
/* YYYY-MM-DD HH:MM:SS.SSS */
|
|
return " (bad date) ";
|
|
}
|
|
days = ms/86400000;
|
|
sod = (ms%86400000)/1000;
|
|
f = (int)(ms%1000);
|
|
|
|
h = sod/3600;
|
|
m = (sod%3600)/60;
|
|
s = sod%60;
|
|
z = days + 719468;
|
|
era = z/146097;
|
|
doe = (unsigned)(z - era*146097);
|
|
yoe = (doe - doe/1460 + doe/36524 - doe/146096)/365;
|
|
y = (int)yoe + era*400;
|
|
doy = doe - (365*yoe + yoe/4 - yoe/100);
|
|
mp = (5*doy + 2)/153;
|
|
D = doy - (153*mp + 2)/5 + 1;
|
|
M = mp + (mp<10 ? 3 : -9);
|
|
Y = y + (M <=2);
|
|
snprintf(zOut, sizeof(zOut),
|
|
"%04d-%02d-%02d %02d:%02d:%02d.%03d",
|
|
Y, M, D, h, m, s, f);
|
|
return zOut;
|
|
}
|
|
|
|
/*
|
|
** Render a single 16-byte tmstmpvfs log record as a line to a CSV file.
|
|
**
|
|
** Columns: tmstmp,fileno,op,pid,pgno,frame,salt,txn
|
|
*/
|
|
static void renderCSV(int iFile, unsigned char *a){
|
|
unsigned int a2, a3;
|
|
int j;
|
|
uint64_t ms;
|
|
|
|
for(ms=0, j=2; j<=7; j++) ms = (ms<<8) + a[j];
|
|
printf("%u.%03u,%d,", (unsigned int)(ms/1000), (unsigned)(ms%1000), iFile);
|
|
for(a2=0, j=8; j<=11; j++) a2 = (a2<<8)+a[j];
|
|
for(a3=0, j=12; j<=15; j++) a3 = (a3<<8)+a[j];
|
|
switch( a[0] ){
|
|
case 0x01: {
|
|
printf("\"open-db\",%u,,,,\r\n",a2);
|
|
break;
|
|
}
|
|
case 0x02: {
|
|
printf("\"open-wal\",%u,,,,\r\n", a2);
|
|
break;
|
|
}
|
|
case 0x03: {
|
|
printf("\"wal-page\",,%u,%u,,%d\r\n", a2, a3, a[1]);
|
|
break;
|
|
}
|
|
case 0x04: {
|
|
printf("\"db-page\",,%u,,,\r\n", a2);
|
|
break;
|
|
}
|
|
case 0x05: {
|
|
printf("\"ckpt-start\",,,,,\r\n");
|
|
break;
|
|
}
|
|
case 0x06: {
|
|
printf("\"ckpt-page\",,%u,%u,,\r\n", a2, a3);
|
|
break;
|
|
}
|
|
case 0x07: {
|
|
printf("\"ckpt-end\",,,,,\r\n");
|
|
break;
|
|
}
|
|
case 0x08: {
|
|
printf("\"wal-reset\",,,,%u,\r\n", a3);
|
|
break;
|
|
}
|
|
case 0x0e: {
|
|
printf("\"close-wal\",,,,,\r\n");
|
|
break;
|
|
}
|
|
case 0x0f: {
|
|
printf("\"close-db\",,,,,\r\n");
|
|
break;
|
|
}
|
|
default: {
|
|
printf("\"invalid-record\",,,,,\r\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Render a single 16-byte tmstmpvfs log record as human-readable text
|
|
** on stdout.
|
|
*/
|
|
static void renderText(unsigned char *a){
|
|
unsigned int a2, a3;
|
|
int j;
|
|
|
|
printf("%s ", decodeTimestamp(a+2));
|
|
for(a2=0, j=8; j<=11; j++) a2 = (a2<<8)+a[j];
|
|
for(a3=0, j=12; j<=15; j++) a3 = (a3<<8)+a[j];
|
|
switch( a[0] ){
|
|
case 0x01: {
|
|
printf("open-db pid %u\n", a2);
|
|
break;
|
|
}
|
|
case 0x02: {
|
|
printf("open-wal pid %u\n", a2);
|
|
break;
|
|
}
|
|
case 0x03: {
|
|
printf("wal-page pgno %-8u frame %-8u%s\n", a2, a3,
|
|
a[1]==1 ? " txn" : "");
|
|
break;
|
|
}
|
|
case 0x04: {
|
|
printf("db-page pgno %-8u\n", a2);
|
|
break;
|
|
}
|
|
case 0x05: {
|
|
printf("ckpt-start\n");
|
|
break;
|
|
}
|
|
case 0x06: {
|
|
printf("ckpt-page pgno %-8u frame %-8u\n", a2, a3);
|
|
break;
|
|
}
|
|
case 0x07: {
|
|
printf("ckpt-end\n");
|
|
break;
|
|
}
|
|
case 0x08: {
|
|
printf("wal-reset salt1 0x%08x\n", a3);
|
|
break;
|
|
}
|
|
case 0x0e: {
|
|
printf("close-wal\n");
|
|
break;
|
|
}
|
|
case 0x0f: {
|
|
printf("close-db\n");
|
|
break;
|
|
}
|
|
default: {
|
|
printf("invalid-record\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usage(const char *argv0){
|
|
printf("Usage: %s [--csv] LOGFILE ...\n", argv0);
|
|
printf("Decode one or more tmstmpvfs log files and display the results\n"
|
|
"on stdout. Render as CSV if the --csv option is used.\n");
|
|
}
|
|
|
|
int main(int argc, char **argv){
|
|
int i;
|
|
FILE *in;
|
|
unsigned char a[16];
|
|
int bCSV = 0;
|
|
const char *z;
|
|
int nFile = 0;
|
|
int iFile;
|
|
for(i=1; i<argc; i++){
|
|
z = argv[i];
|
|
if( z[0]=='-' ){
|
|
if( z[1]=='-' ) z++;
|
|
if( strcmp(z,"-csv")==0 ){
|
|
bCSV = 1;
|
|
}else
|
|
if( strcmp(z,"-help")==0 || strcmp(z,"-?")==0 ){
|
|
usage(argv[0]);
|
|
return 0;
|
|
}else
|
|
{
|
|
printf("unknown command-line option: \"%s\"\n",
|
|
argv[i]);
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}else{
|
|
nFile++;
|
|
}
|
|
}
|
|
if( nFile==0 ){
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
iFile = 0;
|
|
if( bCSV ){
|
|
printf("tmstmp,fileno,op,pid,pgno,frame,salt,txn\r\n");
|
|
}
|
|
for(i=1; i<argc; i++){
|
|
z = argv[i];
|
|
if( z[0]=='-' ) continue;
|
|
in = fopen(z, "rb");
|
|
if( in==0 ){
|
|
printf("%s: can't open\n", z);
|
|
continue;
|
|
}
|
|
iFile++;
|
|
if( nFile>1 && !bCSV ){
|
|
printf("*** %s ***\n", z);
|
|
}
|
|
while( 16==fread(a, 1, 16, in) ){
|
|
if( bCSV ){
|
|
renderCSV(iFile, a);
|
|
}else{
|
|
renderText(a);
|
|
}
|
|
}
|
|
fclose(in);
|
|
}
|
|
return 0;
|
|
}
|