diff options
| author | Nick Barnes | 2012-10-18 17:00:31 +0100 |
|---|---|---|
| committer | Nick Barnes | 2012-10-18 17:00:31 +0100 |
| commit | efdc6b4018a2889d23e47679a2d696aa4fc4c76d (patch) | |
| tree | e8c9856e37dbc443a3b6b2a00866d98cc2bce14d | |
| parent | 2dd20913e4d9bfc948282c11cfa988907ad9a85e (diff) | |
| download | emacs-efdc6b4018a2889d23e47679a2d696aa4fc4c76d.tar.gz emacs-efdc6b4018a2889d23e47679a2d696aa4fc4c76d.zip | |
Re-jigged eventsql so it uses plain-text intermediate files and handles stdin ok.
Copied from Perforce
Change: 179947
ServerID: perforce.ravenbrook.com
| -rw-r--r-- | mps/code/eventcnv.c | 15 | ||||
| -rw-r--r-- | mps/code/eventsql.c | 422 |
2 files changed, 260 insertions, 177 deletions
diff --git a/mps/code/eventcnv.c b/mps/code/eventcnv.c index fcd569add74..42c84c376df 100644 --- a/mps/code/eventcnv.c +++ b/mps/code/eventcnv.c | |||
| @@ -499,7 +499,7 @@ static void readLog(EventProc proc) | |||
| 499 | 499 | ||
| 500 | case EventLabelCode: | 500 | case EventLabelCode: |
| 501 | switch (style) { | 501 | switch (style) { |
| 502 | case '\0': case 'C': | 502 | case '\0': |
| 503 | { | 503 | { |
| 504 | const char *sym = LabelText(proc, event->Label.f1); | 504 | const char *sym = LabelText(proc, event->Label.f1); |
| 505 | printf(style == '\0' ? | 505 | printf(style == '\0' ? |
| @@ -507,12 +507,10 @@ static void readLog(EventProc proc) | |||
| 507 | ", %"PRIuLONGEST", ", | 507 | ", %"PRIuLONGEST", ", |
| 508 | (ulongest_t)event->Label.f0); | 508 | (ulongest_t)event->Label.f0); |
| 509 | if (sym != NULL) { | 509 | if (sym != NULL) { |
| 510 | printStr(sym, (style == 'C')); | 510 | printStr(sym, 0); |
| 511 | } else { | 511 | } else { |
| 512 | printf(style == '\0' ? | 512 | printf("sym %05"PRIXLONGEST , |
| 513 | "sym %05"PRIXLONGEST : | 513 | (ulongest_t)event->Label.f1); |
| 514 | "sym %"PRIXLONGEST"\"", | ||
| 515 | (ulongest_t)event->Label.f1); | ||
| 516 | } | 514 | } |
| 517 | } | 515 | } |
| 518 | break; | 516 | break; |
| @@ -521,6 +519,11 @@ static void readLog(EventProc proc) | |||
| 521 | (ulongest_t)event->Label.f0, | 519 | (ulongest_t)event->Label.f0, |
| 522 | (ulongest_t)event->Label.f1); | 520 | (ulongest_t)event->Label.f1); |
| 523 | break; | 521 | break; |
| 522 | case 'C': | ||
| 523 | printf(", %"PRIuLONGEST", %"PRIuLONGEST, | ||
| 524 | (ulongest_t)event->Label.f0, | ||
| 525 | (ulongest_t)event->Label.f1); | ||
| 526 | break; | ||
| 524 | } | 527 | } |
| 525 | break; | 528 | break; |
| 526 | 529 | ||
diff --git a/mps/code/eventsql.c b/mps/code/eventsql.c index d4401192942..9b069ac65e6 100644 --- a/mps/code/eventsql.c +++ b/mps/code/eventsql.c | |||
| @@ -1,30 +1,38 @@ | |||
| 1 | /* eventsql.c: event log to SQLite importer | 1 | /* eventsql.c: event log to SQLite importer. |
| 2 | * | ||
| 3 | * $Id* | ||
| 4 | * | ||
| 2 | * Copyright (c) 2012 Ravenbrook Limited. See end of file for license. | 5 | * Copyright (c) 2012 Ravenbrook Limited. See end of file for license. |
| 3 | * | 6 | * |
| 4 | * This is a command-line tool that imports events from a binary | 7 | * This is a command-line tool that imports events from a text-format |
| 5 | * format telemetry output file from the MPS into a SQLite database | 8 | * MPS telemetry file into a SQLite database file. |
| 6 | * file. | ||
| 7 | * | ||
| 8 | * The default MPS library will write a telemetry stream to a file called | ||
| 9 | * "mpsio.log" when the environment variable MPS_TELEMETRY_CONTROL is set | ||
| 10 | * to an integer whose bits select event kinds. For example: | ||
| 11 | * | 9 | * |
| 12 | * MPS_TELEMETRY_CONTROL=7 amcss | 10 | * The default MPS library will write a binary-format telemetry file. |
| 11 | * The binary-format file can be converted into a text-format file | ||
| 12 | * using the eventcnv program with the -SC -v options. For | ||
| 13 | * binary-format compatibility, eventcnv has to be run on the same | ||
| 14 | * platform as the MPS. | ||
| 13 | * | 15 | * |
| 14 | * will run the amcss test program and emit a file with event kinds 0, 1, 2. | 16 | * These ASCII text-format files have one line per event, and can be |
| 15 | * The file can then be imported into a SQLite database with this command: | 17 | * manipulated by various splendid systems in the usual Unix way. This |
| 18 | * eventsql program is one such. | ||
| 16 | * | 19 | * |
| 17 | * eventsql | 20 | * Note that eventsql can read streams that come from an MPS running |
| 18 | * | 21 | * on another platform. This is another reason why we use the |
| 19 | * Note that the eventsql program can only read streams that come from an | 22 | * intermediate text-format file, rather than reading the |
| 20 | * MPS compiled on the same platform. | 23 | * binary-format file directly: you can run the MPS and then eventcnv |
| 24 | * on the target platform, then optionally analyse the resulting text | ||
| 25 | * file on some other machine. | ||
| 21 | * | 26 | * |
| 22 | * Each event type gets its own table in the database. These tables | 27 | * Each event type gets its own table in the database. These tables |
| 23 | * are created from the definitions in eventdef.h if they don't | 28 | * are created from the definitions in eventdef.h if they don't |
| 24 | * already exist. Each event becomes a single row in the appropriate | 29 | * already exist. Each event becomes a single row in the appropriate |
| 25 | * table, which has a column for each event parameter, a time column | 30 | * table, which has a column for each event parameter, a time column |
| 26 | * for the event time field, and a log_serial column to identify the | 31 | * for the event time field, and a log_serial column to identify the |
| 27 | * log file. | 32 | * log file. Because the database schema depends on the event |
| 33 | * definitions in eventdef.h, eventsql has to be compiled using the | ||
| 34 | * same event header files as those used to compile the MPS and | ||
| 35 | * eventcnv which generated and processed the telemetry output. | ||
| 28 | * | 36 | * |
| 29 | * The program also creates three other tables: two 'glue' tables | 37 | * The program also creates three other tables: two 'glue' tables |
| 30 | * containing event metadata - event_kind (one row per kind) and | 38 | * containing event metadata - event_kind (one row per kind) and |
| @@ -45,19 +53,19 @@ | |||
| 45 | * progress (one dot is 100k events). | 53 | * progress (one dot is 100k events). |
| 46 | * | 54 | * |
| 47 | * -t (test): Run unit tests on parts of eventsql. There aren't many | 55 | * -t (test): Run unit tests on parts of eventsql. There aren't many |
| 48 | * of these. | 56 | * of these. TODO: write more unit tests. |
| 49 | * | 57 | * |
| 50 | * -f (force): Import the events to SQL even if the SQL database | 58 | * -f (force): Import the events to SQL even if the SQL database |
| 51 | * already includes a record of importing this event log file (matched by | 59 | * already includes a record of importing a matching log file. |
| 52 | * size, modtime, and filesystem ID. | ||
| 53 | * | 60 | * |
| 54 | * -r (rebuild): Drop the glue tables from SQL, which will force them | 61 | * -r (rebuild): Drop the glue tables from SQL, which will force them |
| 55 | * to be recreated. Important if you change event types or kinds in | 62 | * to be recreated. Important if you change event types or kinds in |
| 56 | * eventdef.h. | 63 | * eventdef.h. |
| 57 | * | 64 | * |
| 58 | * -l <logfile>: Import events from the named logfile. If not | 65 | * -l <logfile>: Import events from the named logfile. Defaults to |
| 59 | * specified, eventsql will use the MPS_TELEMETRY_FILENAME environment | 66 | * stdin. If the specified file (matched by size and modtime) has |
| 60 | * variable, and default to mpsio.log. | 67 | * previously been imported to the same database, it will not be |
| 68 | * imported again unless -f is specified. | ||
| 61 | * | 69 | * |
| 62 | * -d <database>: Import events to the named database file. If not | 70 | * -d <database>: Import events to the named database file. If not |
| 63 | * specified, eventsql will use the MPS_EVENT_DATABASE environment | 71 | * specified, eventsql will use the MPS_EVENT_DATABASE environment |
| @@ -66,9 +74,10 @@ | |||
| 66 | * $Id$ | 74 | * $Id$ |
| 67 | */ | 75 | */ |
| 68 | 76 | ||
| 77 | #include "misc.h" | ||
| 69 | #include "config.h" | 78 | #include "config.h" |
| 70 | #include "eventdef.h" | 79 | #include "eventdef.h" |
| 71 | #include "eventpro.h" | 80 | #include "eventcom.h" |
| 72 | 81 | ||
| 73 | #include <stdio.h> | 82 | #include <stdio.h> |
| 74 | #include <stdlib.h> | 83 | #include <stdlib.h> |
| @@ -79,9 +88,6 @@ | |||
| 79 | #define DATABASE_NAME_ENVAR "MPS_EVENT_DATABASE" | 88 | #define DATABASE_NAME_ENVAR "MPS_EVENT_DATABASE" |
| 80 | #define DEFAULT_DATABASE_NAME "mpsevent.db" | 89 | #define DEFAULT_DATABASE_NAME "mpsevent.db" |
| 81 | 90 | ||
| 82 | #define TELEMETRY_FILENAME_ENVAR "MPS_TELEMETRY_FILENAME" | ||
| 83 | #define DEFAULT_TELEMETRY_FILENAME "mpsio.log" | ||
| 84 | |||
| 85 | /* we output rows of dots. One dot per SMALL_TICK events, | 91 | /* we output rows of dots. One dot per SMALL_TICK events, |
| 86 | * BIG_TICK dots per row. */ | 92 | * BIG_TICK dots per row. */ |
| 87 | 93 | ||
| @@ -231,7 +237,7 @@ static sqlite3 *openDatabase(void) | |||
| 231 | if (res != SQLITE_OK) | 237 | if (res != SQLITE_OK) |
| 232 | sqlite_error(res, db, "Opening %s failed", databaseName); | 238 | sqlite_error(res, db, "Opening %s failed", databaseName); |
| 233 | 239 | ||
| 234 | log(LOG_ALWAYS, "Writing to %s.",databaseName); | 240 | log(LOG_OFTEN, "Writing to %s.",databaseName); |
| 235 | 241 | ||
| 236 | return db; | 242 | return db; |
| 237 | } | 243 | } |
| @@ -243,7 +249,7 @@ static void closeDatabase(sqlite3 *db) | |||
| 243 | int res = sqlite3_close(db); | 249 | int res = sqlite3_close(db); |
| 244 | if (res != SQLITE_OK) | 250 | if (res != SQLITE_OK) |
| 245 | sqlite_error(res, db, "Closing database failed"); | 251 | sqlite_error(res, db, "Closing database failed"); |
| 246 | log(LOG_ALWAYS, "Closed %s.", databaseName); | 252 | log(LOG_SOMETIMES, "Closed %s.", databaseName); |
| 247 | } | 253 | } |
| 248 | 254 | ||
| 249 | /* We need to be able to test for the existence of a table. The | 255 | /* We need to be able to test for the existence of a table. The |
| @@ -268,7 +274,7 @@ static int tableExists(sqlite3* db, const char *tableName) | |||
| 268 | if (!sql) | 274 | if (!sql) |
| 269 | error("Out of memory."); | 275 | error("Out of memory."); |
| 270 | sprintf(sql, format, tableName); | 276 | sprintf(sql, format, tableName); |
| 271 | log(LOG_RARELY, "Testing for existence of table '%s' with SQL: %s", tableName, sql); | 277 | log(LOG_SELDOM, "Testing for existence of table '%s' with SQL: %s", tableName, sql); |
| 272 | res = sqlite3_exec(db, | 278 | res = sqlite3_exec(db, |
| 273 | sql, | 279 | sql, |
| 274 | NULL, /* put in a callback here if we really want to know the number of rows */ | 280 | NULL, /* put in a callback here if we really want to know the number of rows */ |
| @@ -278,12 +284,12 @@ static int tableExists(sqlite3* db, const char *tableName) | |||
| 278 | 284 | ||
| 279 | switch(res) { | 285 | switch(res) { |
| 280 | case SQLITE_OK: | 286 | case SQLITE_OK: |
| 281 | log(LOG_RARELY, "Table '%s' exists.", tableName); | 287 | log(LOG_SELDOM, "Table '%s' exists.", tableName); |
| 282 | 288 | ||
| 283 | return 1; /* table exists */ | 289 | return 1; /* table exists */ |
| 284 | break; | 290 | break; |
| 285 | case SQLITE_ERROR: | 291 | case SQLITE_ERROR: |
| 286 | log(LOG_RARELY, "Table '%s' does not exist.", tableName); | 292 | log(LOG_SELDOM, "Table '%s' does not exist.", tableName); |
| 287 | return 0; /* table does not exist; we can | 293 | return 0; /* table does not exist; we can |
| 288 | probably do a better test for this case. */ | 294 | probably do a better test for this case. */ |
| 289 | break; | 295 | break; |
| @@ -344,6 +350,7 @@ static void runStatement(sqlite3 *db, | |||
| 344 | const char *description) | 350 | const char *description) |
| 345 | { | 351 | { |
| 346 | int res; | 352 | int res; |
| 353 | log(LOG_SELDOM, "%s: %s", description, sql); | ||
| 347 | res = sqlite3_exec(db, | 354 | res = sqlite3_exec(db, |
| 348 | sql, | 355 | sql, |
| 349 | NULL, /* No callback */ | 356 | NULL, /* No callback */ |
| @@ -366,70 +373,74 @@ static unsigned long logSerial = 0; | |||
| 366 | static void registerLogFile(sqlite3 *db, | 373 | static void registerLogFile(sqlite3 *db, |
| 367 | const char *filename) | 374 | const char *filename) |
| 368 | { | 375 | { |
| 369 | struct stat st; | ||
| 370 | sqlite3_stmt *statement; | 376 | sqlite3_stmt *statement; |
| 371 | int res; | 377 | int res; |
| 372 | const unsigned char *name; | 378 | const unsigned char *name; |
| 373 | unsigned long completed; | 379 | unsigned long completed; |
| 374 | 380 | unsigned long long file_size; | |
| 375 | res = stat(filename, &st); | 381 | unsigned long long file_modtime; |
| 376 | if (res != 0) | 382 | |
| 377 | error("Couldn't stat() %s", filename); | 383 | if (filename) { |
| 378 | 384 | struct stat st; | |
| 379 | statement = prepareStatement(db, | 385 | res = stat(filename, &st); |
| 380 | "SELECT name, serial, completed FROM event_log" | 386 | if (res != 0) |
| 381 | " WHERE file_id = ? AND size = ? AND modtime = ?"); | 387 | error("Couldn't stat() %s", filename); |
| 382 | res = sqlite3_bind_int64(statement, 1, st.st_ino); | 388 | file_size = st.st_size; |
| 383 | if (res != SQLITE_OK) | 389 | file_modtime = st.st_mtime; |
| 384 | sqlite_error(res, db, "event_log bind of file_id failed."); | 390 | |
| 385 | res = sqlite3_bind_int64(statement, 2, st.st_size); | 391 | statement = prepareStatement(db, |
| 386 | if (res != SQLITE_OK) | 392 | "SELECT name, serial, completed FROM event_log" |
| 387 | sqlite_error(res, db, "event_log bind of size failed."); | 393 | " WHERE size = ? AND modtime = ?"); |
| 388 | res = sqlite3_bind_int64(statement, 3, st.st_mtime); | 394 | res = sqlite3_bind_int64(statement, 1, file_size); |
| 389 | if (res != SQLITE_OK) | 395 | if (res != SQLITE_OK) |
| 390 | sqlite_error(res, db, "event_log bind of modtime failed."); | 396 | sqlite_error(res, db, "event_log bind of size failed."); |
| 391 | 397 | res = sqlite3_bind_int64(statement, 2, file_modtime); | |
| 392 | res = sqlite3_step(statement); | 398 | if (res != SQLITE_OK) |
| 393 | switch(res) { | 399 | sqlite_error(res, db, "event_log bind of modtime failed."); |
| 394 | case SQLITE_DONE: | 400 | res = sqlite3_step(statement); |
| 395 | log(LOG_SOMETIMES, "No log file matching '%s' found in database.", filename); | 401 | switch(res) { |
| 396 | break; | 402 | case SQLITE_DONE: |
| 397 | case SQLITE_ROW: | 403 | log(LOG_SOMETIMES, "No log file matching '%s' found in database.", filename); |
| 398 | name = sqlite3_column_text(statement, 0); | 404 | break; |
| 399 | logSerial = sqlite3_column_int(statement, 1); | 405 | case SQLITE_ROW: |
| 400 | completed = sqlite3_column_int(statement, 2); | 406 | name = sqlite3_column_text(statement, 0); |
| 401 | log(LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %lu, completed %lu).", | 407 | logSerial = sqlite3_column_int(statement, 1); |
| 402 | filename, name, logSerial, completed); | 408 | completed = sqlite3_column_int(statement, 2); |
| 403 | if (!force) { | 409 | log(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %lu, completed %lu).", |
| 404 | log(LOG_ALWAYS, "Exiting. Specify -f to force events into SQL anyway."); | 410 | filename, name, logSerial, completed); |
| 405 | exit(0); | 411 | if (force) { |
| 412 | log(LOG_OFTEN, "Continuing anyway because -f specified."); | ||
| 413 | } else { | ||
| 414 | log(LOG_ALWAYS, "Exiting. Specify -f to force events into SQL anyway."); | ||
| 415 | exit(0); | ||
| 416 | } | ||
| 417 | break; | ||
| 418 | default: | ||
| 419 | sqlite_error(res, db, "select from event_log failed."); | ||
| 406 | } | 420 | } |
| 407 | log(LOG_ALWAYS, "Continuing anyway because -f specified."); | 421 | finalizeStatement(db, statement); |
| 408 | break; | 422 | } else { /* stdin */ |
| 409 | default: | 423 | filename = "<stdin>"; |
| 410 | sqlite_error(res, db, "select from event_log failed."); | 424 | file_size = 0; |
| 425 | file_modtime = 0; | ||
| 411 | } | 426 | } |
| 412 | finalizeStatement(db, statement); | ||
| 413 | statement = prepareStatement(db, | 427 | statement = prepareStatement(db, |
| 414 | "INSERT into event_log (name, file_id, size, modtime, completed)" | 428 | "INSERT into event_log (name, size, modtime, completed)" |
| 415 | " VALUES (?, ?, ?, ?, 0)"); | 429 | " VALUES (?, ?, ?, 0)"); |
| 416 | res = sqlite3_bind_text(statement, 1, filename, -1, SQLITE_STATIC); | 430 | res = sqlite3_bind_text(statement, 1, filename, -1, SQLITE_STATIC); |
| 417 | if (res != SQLITE_OK) | 431 | if (res != SQLITE_OK) |
| 418 | sqlite_error(res, db, "event_log insert bind of name failed."); | 432 | sqlite_error(res, db, "event_log insert bind of name failed."); |
| 419 | res = sqlite3_bind_int64(statement, 2, st.st_ino); | 433 | res = sqlite3_bind_int64(statement, 2, file_size); |
| 420 | if (res != SQLITE_OK) | ||
| 421 | sqlite_error(res, db, "event_log insert bind of file_id failed."); | ||
| 422 | res = sqlite3_bind_int64(statement, 3, st.st_size); | ||
| 423 | if (res != SQLITE_OK) | 434 | if (res != SQLITE_OK) |
| 424 | sqlite_error(res, db, "event_log insert bind of size failed."); | 435 | sqlite_error(res, db, "event_log insert bind of size failed."); |
| 425 | res = sqlite3_bind_int64(statement, 4, st.st_mtime); | 436 | res = sqlite3_bind_int64(statement, 3, file_modtime); |
| 426 | if (res != SQLITE_OK) | 437 | if (res != SQLITE_OK) |
| 427 | sqlite_error(res, db, "event_log insert bind of modtime failed."); | 438 | sqlite_error(res, db, "event_log insert bind of modtime failed."); |
| 428 | res = sqlite3_step(statement); | 439 | res = sqlite3_step(statement); |
| 429 | if (res != SQLITE_DONE) | 440 | if (res != SQLITE_DONE) |
| 430 | sqlite_error(res, db, "insert into event_log failed."); | 441 | sqlite_error(res, db, "insert into event_log failed."); |
| 431 | logSerial = sqlite3_last_insert_rowid(db); | 442 | logSerial = sqlite3_last_insert_rowid(db); |
| 432 | log(LOG_SOMETIMES, "Log file added to event_log with serial %lu", | 443 | log(LOG_SOMETIMES, "Log file %s added to event_log with serial %lu", |
| 433 | filename, logSerial); | 444 | filename, logSerial); |
| 434 | finalizeStatement(db, statement); | 445 | finalizeStatement(db, statement); |
| 435 | } | 446 | } |
| @@ -451,7 +462,7 @@ static void logFileCompleted(sqlite3 *db, | |||
| 451 | res = sqlite3_step(statement); | 462 | res = sqlite3_step(statement); |
| 452 | if (res != SQLITE_DONE) | 463 | if (res != SQLITE_DONE) |
| 453 | sqlite_error(res, db, "insert into event_log failed."); | 464 | sqlite_error(res, db, "insert into event_log failed."); |
| 454 | log(LOG_OFTEN, "Marked in event_log: %lu events", completed); | 465 | log(LOG_SOMETIMES, "Marked in event_log: %lu events", completed); |
| 455 | finalizeStatement(db, statement); | 466 | finalizeStatement(db, statement); |
| 456 | } | 467 | } |
| 457 | 468 | ||
| @@ -488,7 +499,6 @@ const char *createStatements[] = { | |||
| 488 | " FOREIGN KEY (kind) REFERENCES event_kind(enum));", | 499 | " FOREIGN KEY (kind) REFERENCES event_kind(enum));", |
| 489 | 500 | ||
| 490 | "CREATE TABLE IF NOT EXISTS event_log (name TEXT," | 501 | "CREATE TABLE IF NOT EXISTS event_log (name TEXT," |
| 491 | " file_id INTEGER," | ||
| 492 | " size INTEGER," | 502 | " size INTEGER," |
| 493 | " modtime INTEGER," | 503 | " modtime INTEGER," |
| 494 | " completed INTEGER," | 504 | " completed INTEGER," |
| @@ -502,9 +512,9 @@ EVENT_LIST(EVENT_TABLE_CREATE, X) | |||
| 502 | static void makeTables(sqlite3 *db) | 512 | static void makeTables(sqlite3 *db) |
| 503 | { | 513 | { |
| 504 | int i; | 514 | int i; |
| 515 | log(LOG_SOMETIMES, "Creating tables."); | ||
| 505 | 516 | ||
| 506 | for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { | 517 | for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { |
| 507 | log(LOG_SOMETIMES, "Creating tables. SQL command: %s", createStatements[i]); | ||
| 508 | runStatement(db, createStatements[i], "Table creation"); | 518 | runStatement(db, createStatements[i], "Table creation"); |
| 509 | } | 519 | } |
| 510 | } | 520 | } |
| @@ -581,8 +591,8 @@ static void dropGlueTables(sqlite3 *db) | |||
| 581 | static void fillGlueTables(sqlite3 *db) | 591 | static void fillGlueTables(sqlite3 *db) |
| 582 | { | 592 | { |
| 583 | int i; | 593 | int i; |
| 584 | Res res; | ||
| 585 | sqlite3_stmt *statement; | 594 | sqlite3_stmt *statement; |
| 595 | int res; | ||
| 586 | 596 | ||
| 587 | statement = prepareStatement(db, | 597 | statement = prepareStatement(db, |
| 588 | "INSERT OR IGNORE INTO event_kind (name, description, enum)" | 598 | "INSERT OR IGNORE INTO event_kind (name, description, enum)" |
| @@ -622,61 +632,100 @@ static void fillGlueTables(sqlite3 *db) | |||
| 622 | #define EVENT_TYPE_FINALIZE_STATEMENT(X, name, code, always, kind) \ | 632 | #define EVENT_TYPE_FINALIZE_STATEMENT(X, name, code, always, kind) \ |
| 623 | finalizeStatement(db, stmt_##name); | 633 | finalizeStatement(db, stmt_##name); |
| 624 | 634 | ||
| 625 | #define EVENT_PARAM_BIND_INTEGER(name, index, sort, ident) \ | 635 | #define EVENT_PARAM_BIND_A bind_int |
| 626 | res = sqlite3_bind_int64(statement, index+1, (unsigned long) event->name.f##index); | 636 | #define EVENT_PARAM_BIND_P bind_int |
| 627 | 637 | #define EVENT_PARAM_BIND_U bind_int | |
| 628 | #define EVENT_PARAM_BIND_REAL(name, index, sort, ident) \ | 638 | #define EVENT_PARAM_BIND_W bind_int |
| 629 | res = sqlite3_bind_double(statement, index+1, event->name.f##index); | 639 | #define EVENT_PARAM_BIND_D bind_real |
| 630 | 640 | #define EVENT_PARAM_BIND_S bind_text | |
| 631 | #define EVENT_PARAM_BIND_TEXT(name, index, sort, ident) \ | 641 | #define EVENT_PARAM_BIND_B bind_int |
| 632 | res = sqlite3_bind_text(statement, index+1, event->name.f##index, -1, SQLITE_STATIC); | ||
| 633 | |||
| 634 | #define EVENT_PARAM_BIND_A EVENT_PARAM_BIND_INTEGER | ||
| 635 | #define EVENT_PARAM_BIND_P EVENT_PARAM_BIND_INTEGER | ||
| 636 | #define EVENT_PARAM_BIND_U EVENT_PARAM_BIND_INTEGER | ||
| 637 | #define EVENT_PARAM_BIND_W EVENT_PARAM_BIND_INTEGER | ||
| 638 | #define EVENT_PARAM_BIND_D EVENT_PARAM_BIND_REAL | ||
| 639 | #define EVENT_PARAM_BIND_S EVENT_PARAM_BIND_TEXT | ||
| 640 | #define EVENT_PARAM_BIND_B EVENT_PARAM_BIND_INTEGER | ||
| 641 | 642 | ||
| 642 | #define EVENT_PARAM_BIND(name, index, sort, ident) \ | 643 | #define EVENT_PARAM_BIND(X, index, sort, ident) \ |
| 643 | EVENT_PARAM_BIND_##sort (name, index, sort, ident) \ | 644 | p = EVENT_PARAM_BIND_##sort (db, statement, eventCount, index+1, p); \ |
| 644 | if (res != SQLITE_OK) \ | ||
| 645 | sqlite_error(res, db, "Event " #name " bind of ident " #ident "failed."); \ | ||
| 646 | last_index = index+1; | 645 | last_index = index+1; |
| 647 | 646 | ||
| 648 | |||
| 649 | #define EVENT_TYPE_WRITE_SQL(X, name, code, always, kind) \ | 647 | #define EVENT_TYPE_WRITE_SQL(X, name, code, always, kind) \ |
| 650 | case code: \ | 648 | case code: \ |
| 651 | { \ | 649 | statement = stmt_##name; \ |
| 652 | sqlite3_stmt *statement = stmt_##name; \ | ||
| 653 | int last_index = 0; \ | ||
| 654 | int res; \ | ||
| 655 | /* bind all the parameters of this particular event with macro magic. */ \ | 650 | /* bind all the parameters of this particular event with macro magic. */ \ |
| 656 | EVENT_##name##_PARAMS(EVENT_PARAM_BIND, name) \ | 651 | EVENT_##name##_PARAMS(EVENT_PARAM_BIND, X) \ |
| 657 | /* bind the fields we store for every event */ \ | 652 | break; |
| 658 | res = sqlite3_bind_int64(statement, last_index+1, logSerial); \ | 653 | |
| 659 | if (res != SQLITE_OK) \ | 654 | static char *bind_int(sqlite3 *db, sqlite3_stmt *stmt, unsigned long long count, int index, char *p) |
| 660 | sqlite_error(res, db, "Event " #name " bind of log_serial failed."); \ | 655 | { |
| 661 | res = sqlite3_bind_int64(statement, last_index+2, event->any.clock); \ | 656 | char *q; |
| 662 | if (res != SQLITE_OK) \ | 657 | long long val; |
| 663 | sqlite_error(res, db, "Event " #name " bind of clock failed."); \ | 658 | int res; |
| 664 | res = sqlite3_step(statement); \ | 659 | |
| 665 | if (res != SQLITE_DONE) \ | 660 | if ((p[0] != ',') || (p[1] != ' ')) |
| 666 | sqlite_error(res, db, "insert of event \"" #name "\" failed."); \ | 661 | error("event %llu field %d not preceded by \", \": %s", |
| 667 | res = sqlite3_reset(statement); \ | 662 | count, index, p); |
| 668 | if (res != SQLITE_OK) \ | 663 | |
| 669 | sqlite_error(res, db, "Couldn't reset insert statement for \"" #name "\"."); \ | 664 | p += 2; |
| 670 | } \ | 665 | val = strtoll(p, &q, 0); |
| 671 | break; | 666 | if (q == p) |
| 667 | error("event %llu field %d not an integer: %s", | ||
| 668 | count, index, p); | ||
| 669 | |||
| 670 | res = sqlite3_bind_int64(stmt, index, val); | ||
| 671 | if (res != SQLITE_OK) | ||
| 672 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); | ||
| 673 | return q; | ||
| 674 | } | ||
| 675 | |||
| 676 | static char *bind_real(sqlite3 *db, sqlite3_stmt *stmt, unsigned long long count, int index, char *p) | ||
| 677 | { | ||
| 678 | char *q; | ||
| 679 | double val; | ||
| 680 | int res; | ||
| 681 | |||
| 682 | if ((p[0] != ',') || (p[1] != ' ')) | ||
| 683 | error("event %llu field %d not preceded by \", \": %s", | ||
| 684 | count, index, p); | ||
| 685 | |||
| 686 | p += 2; | ||
| 687 | val = strtod(p, &q); | ||
| 688 | if (q == p) | ||
| 689 | error("event %llu field %d not a floating-point value: %s", | ||
| 690 | count, index, p); | ||
| 691 | |||
| 692 | res = sqlite3_bind_double(stmt, index, val); | ||
| 693 | if (res != SQLITE_OK) | ||
| 694 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); | ||
| 695 | return q; | ||
| 696 | } | ||
| 697 | |||
| 698 | static char *bind_text(sqlite3 *db, sqlite3_stmt *stmt, unsigned long long count, int index, char *p) | ||
| 699 | { | ||
| 700 | char *q; | ||
| 701 | int res; | ||
| 702 | |||
| 703 | if ((p[0] != ',') || (p[1] != ' ') || (p[2] != '"')) | ||
| 704 | error("event %llu string field %d not preceded by \", \\\"\": %s", | ||
| 705 | count, index, p); | ||
| 672 | 706 | ||
| 673 | /* readLog -- read and parse log | 707 | p += 3; |
| 708 | q = p; | ||
| 709 | while((*q != '\n') && (*q != '\0')) { | ||
| 710 | ++ q; | ||
| 711 | } | ||
| 712 | if ((q == p) || (q[-1] != '"')) | ||
| 713 | error("event %llu string field %d has no closing quote mark.", | ||
| 714 | count, index); | ||
| 715 | |||
| 716 | res = sqlite3_bind_text(stmt, index, p, q-p-1, SQLITE_STATIC); | ||
| 717 | if (res != SQLITE_OK) | ||
| 718 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); | ||
| 719 | return q; | ||
| 720 | } | ||
| 721 | |||
| 722 | /* readLog -- read and parse log. Returns the number of events written. | ||
| 674 | */ | 723 | */ |
| 675 | 724 | ||
| 676 | static void readLog(EventProc proc, | 725 | static unsigned long long readLog(FILE *input, |
| 677 | sqlite3 *db) | 726 | sqlite3 *db) |
| 678 | { | 727 | { |
| 679 | size_t eventCount = 0; | 728 | unsigned long long eventCount = 0; |
| 680 | 729 | ||
| 681 | /* declare statements for every event type */ | 730 | /* declare statements for every event type */ |
| 682 | EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X); | 731 | EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X); |
| @@ -687,28 +736,70 @@ static void readLog(EventProc proc, | |||
| 687 | runStatement(db, "BEGIN", "Transaction start"); | 736 | runStatement(db, "BEGIN", "Transaction start"); |
| 688 | 737 | ||
| 689 | while (TRUE) { /* loop for each event */ | 738 | while (TRUE) { /* loop for each event */ |
| 690 | Event event; | 739 | char line[1024]; |
| 691 | EventCode code; | 740 | char *p; |
| 692 | Res res; | 741 | char *q; |
| 693 | 742 | int last_index=0; | |
| 694 | /* Read and parse event. */ | 743 | sqlite3_stmt *statement; |
| 695 | res = EventRead(&event, proc); | 744 | int res; |
| 696 | if (res == ResFAIL) break; /* eof */ | 745 | unsigned long long clock; |
| 697 | if (res != ResOK) error("Truncated log"); | 746 | int code; |
| 698 | code = event->any.code; | 747 | |
| 699 | 748 | p = fgets(line, 1024, input); | |
| 749 | if (!p) { | ||
| 750 | if (feof(input)) | ||
| 751 | break; | ||
| 752 | else | ||
| 753 | error("Couldn't read line after event %llu", eventCount); | ||
| 754 | } | ||
| 755 | |||
| 756 | eventCount++; | ||
| 757 | |||
| 758 | clock = strtoll(p, &q, 16); | ||
| 759 | if (q == p) | ||
| 760 | error("event %llu clock field not a hex integer: %s", | ||
| 761 | eventCount, p); | ||
| 762 | |||
| 763 | if ((q[0] != ',') || (q[1] != ' ')) | ||
| 764 | error("event %llu code field not preceded by \", \": %s", | ||
| 765 | eventCount, q); | ||
| 766 | |||
| 767 | p = q + 2; | ||
| 768 | |||
| 769 | code = strtol(p, &q, 0); | ||
| 770 | if (q == p) | ||
| 771 | error("event %llu code field %d not an integer: %s", | ||
| 772 | eventCount, index, p); | ||
| 773 | p = q; | ||
| 700 | /* Write event to SQLite. */ | 774 | /* Write event to SQLite. */ |
| 701 | switch (code) { | 775 | switch (code) { |
| 776 | /* this macro sets statement and last_index */ | ||
| 702 | EVENT_LIST(EVENT_TYPE_WRITE_SQL, X); | 777 | EVENT_LIST(EVENT_TYPE_WRITE_SQL, X); |
| 778 | default: | ||
| 779 | error("Event %llu has Unknown event code %d", eventCount, code); | ||
| 703 | } | 780 | } |
| 704 | EventDestroy(proc, event); | 781 | /* bind the fields we store for every event */ \ |
| 705 | eventCount++; | 782 | res = sqlite3_bind_int64(statement, last_index+1, logSerial); |
| 783 | if (res != SQLITE_OK) | ||
| 784 | sqlite_error(res, db, "Event %llu bind of log_serial failed.", eventCount); | ||
| 785 | res = sqlite3_bind_int64(statement, last_index+2, clock); | ||
| 786 | if (res != SQLITE_OK) | ||
| 787 | sqlite_error(res, db, "Event %llu bind of clock failed.", eventCount); | ||
| 788 | res = sqlite3_step(statement); | ||
| 789 | if (res != SQLITE_DONE) | ||
| 790 | sqlite_error(res, db, "insert of event %llu failed.", eventCount); | ||
| 791 | res = sqlite3_reset(statement); | ||
| 792 | if (res != SQLITE_OK) | ||
| 793 | sqlite_error(res, db, "Couldn't reset insert statement of event %llu", eventCount); | ||
| 794 | |||
| 706 | if (verbosity > LOG_ALWAYS) { | 795 | if (verbosity > LOG_ALWAYS) { |
| 707 | if ((eventCount % SMALL_TICK) == 0) { | 796 | if ((eventCount % SMALL_TICK) == 0) { |
| 708 | printf("."); | 797 | printf("."); |
| 709 | fflush(stdout); | 798 | fflush(stdout); |
| 710 | if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) { | 799 | if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) { |
| 711 | log(LOG_OFTEN, "%lu events.", (unsigned long)eventCount); | 800 | printf("\n"); |
| 801 | fflush(stdout); | ||
| 802 | log(LOG_SOMETIMES, "%lu events.", (unsigned long)eventCount); | ||
| 712 | } | 803 | } |
| 713 | } | 804 | } |
| 714 | } | 805 | } |
| @@ -718,64 +809,48 @@ static void readLog(EventProc proc, | |||
| 718 | fflush(stdout); | 809 | fflush(stdout); |
| 719 | } | 810 | } |
| 720 | runStatement(db, "COMMIT", "Transaction finish"); | 811 | runStatement(db, "COMMIT", "Transaction finish"); |
| 721 | |||
| 722 | log(LOG_ALWAYS, "Wrote %lu events to SQL.", (unsigned long)eventCount); | ||
| 723 | logFileCompleted(db, eventCount); | 812 | logFileCompleted(db, eventCount); |
| 724 | 813 | ||
| 725 | /* finalize all the statements */ | 814 | /* finalize all the statements */ |
| 726 | EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X); | 815 | EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X); |
| 727 | } | ||
| 728 | 816 | ||
| 729 | static Res logReader(void *file, void *p, size_t len) | 817 | return eventCount; |
| 730 | { | ||
| 731 | size_t n; | ||
| 732 | |||
| 733 | n = fread(p, 1, len, (FILE *)file); | ||
| 734 | return (n < len) ? (feof((FILE *)file) ? ResFAIL : ResIO) : ResOK; | ||
| 735 | } | 818 | } |
| 736 | 819 | ||
| 737 | static FILE *openLog(sqlite3 *db) | 820 | static FILE *openLog(sqlite3 *db) |
| 738 | { | 821 | { |
| 739 | FILE *input; | 822 | FILE *input; |
| 740 | 823 | ||
| 824 | registerLogFile(db, logFileName); | ||
| 741 | if (!logFileName) { | 825 | if (!logFileName) { |
| 742 | logFileName = getenv(TELEMETRY_FILENAME_ENVAR); | 826 | input = stdin; |
| 743 | if(logFileName == NULL) | 827 | logFileName = "<stdin>"; |
| 744 | logFileName = DEFAULT_TELEMETRY_FILENAME; | 828 | } else { |
| 829 | input = fopen(logFileName, "r"); | ||
| 830 | if (input == NULL) | ||
| 831 | error("unable to open %s", logFileName); | ||
| 745 | } | 832 | } |
| 746 | 833 | ||
| 747 | registerLogFile(db, logFileName); | 834 | log(LOG_OFTEN, "Reading %s.", logFileName ? logFileName : "standard input"); |
| 748 | input = fopen(logFileName, "rb"); | ||
| 749 | |||
| 750 | if (input == NULL) | ||
| 751 | error("unable to open %s", logFileName); | ||
| 752 | |||
| 753 | log(LOG_ALWAYS, "Reading %s.", logFileName); | ||
| 754 | 835 | ||
| 755 | return input; | 836 | return input; |
| 756 | } | 837 | } |
| 757 | 838 | ||
| 758 | static void writeEventsToSQL(sqlite3 *db) | 839 | static unsigned long long writeEventsToSQL(sqlite3 *db) |
| 759 | { | 840 | { |
| 760 | Res res; | ||
| 761 | EventProc proc; | ||
| 762 | FILE *input; | 841 | FILE *input; |
| 763 | 842 | unsigned long long count; | |
| 764 | input = openLog(db); | 843 | input = openLog(db); |
| 765 | res = EventProcCreate(&proc, logReader, (void *)input); | 844 | count = readLog(input, db); |
| 766 | if (res != ResOK) | ||
| 767 | error("Can't init EventProc module: error %d.", res); | ||
| 768 | |||
| 769 | readLog(proc, db); | ||
| 770 | |||
| 771 | EventProcDestroy(proc); | ||
| 772 | (void)fclose(input); | 845 | (void)fclose(input); |
| 846 | return count; | ||
| 773 | } | 847 | } |
| 774 | 848 | ||
| 775 | 849 | ||
| 776 | int main(int argc, char *argv[]) | 850 | int main(int argc, char *argv[]) |
| 777 | { | 851 | { |
| 778 | sqlite3 *db; | 852 | sqlite3 *db; |
| 853 | unsigned long long count; | ||
| 779 | 854 | ||
| 780 | parseArgs(argc, argv); | 855 | parseArgs(argc, argv); |
| 781 | 856 | ||
| @@ -785,7 +860,12 @@ int main(int argc, char *argv[]) | |||
| 785 | } | 860 | } |
| 786 | makeTables(db); | 861 | makeTables(db); |
| 787 | fillGlueTables(db); | 862 | fillGlueTables(db); |
| 788 | writeEventsToSQL(db); | 863 | count = writeEventsToSQL(db); |
| 864 | log(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %lu.", | ||
| 865 | (unsigned long)count, | ||
| 866 | logFileName, | ||
| 867 | databaseName, | ||
| 868 | logSerial); | ||
| 789 | 869 | ||
| 790 | if (runTests) { | 870 | if (runTests) { |
| 791 | /* TODO: more unit tests in here */ | 871 | /* TODO: more unit tests in here */ |