diff --git a/src/runtime/database.cpp b/src/runtime/database.cpp index 64d4ebf7e..0103bb8f3 100644 --- a/src/runtime/database.cpp +++ b/src/runtime/database.cpp @@ -80,6 +80,7 @@ struct Database::detail { sqlite3_stmt *tag_job; sqlite3_stmt *get_tags; sqlite3_stmt *get_all_tags; + sqlite3_stmt *get_all_runs; sqlite3_stmt *get_edges; sqlite3_stmt *get_file_dependency; sqlite3_stmt *get_output_files; @@ -125,6 +126,7 @@ struct Database::detail { tag_job(0), get_tags(0), get_all_tags(0), + get_all_runs(0), get_edges(0), get_file_dependency(0), get_interleaved_output(0) {} @@ -396,6 +398,7 @@ std::string Database::open(bool wait, bool memory, bool tty) { const char *sql_tag_job = "insert into tags(job_id, uri, content) values(?, ?, ?)"; const char *sql_get_tags = "select job_id, uri, content from tags where job_id=?"; const char *sql_get_all_tags = "select job_id, uri, content from tags"; + const char *sql_get_all_runs = "select run_id, time, cmdline from runs order by time ASC"; const char *sql_get_edges = "select distinct user.job_id as user, used.job_id as used" " from filetree user, filetree used" @@ -470,6 +473,7 @@ std::string Database::open(bool wait, bool memory, bool tty) { PREPARE(sql_tag_job, tag_job); PREPARE(sql_get_tags, get_tags); PREPARE(sql_get_all_tags, get_all_tags); + PREPARE(sql_get_all_runs, get_all_runs); PREPARE(sql_get_edges, get_edges); PREPARE(sql_get_file_dependency, get_file_dependency); PREPARE(sql_get_output_files, get_output_files); @@ -528,6 +532,7 @@ void Database::close() { FINALIZE(tag_job); FINALIZE(get_tags); FINALIZE(get_all_tags); + FINALIZE(get_all_runs); FINALIZE(get_edges); FINALIZE(get_file_dependency); FINALIZE(get_output_files); @@ -1088,14 +1093,8 @@ std::string Time::as_string() const { char buf[100]; struct tm tm; time_t time = t / 1000000000; - long subs = t % 1000000000; - if (subs < 0) { - --time; - subs += 1000000000; - } localtime_r(&time, &tm); - strftime(buf, sizeof(buf) - 10, "%Y-%m-%d %H:%M:%S.", &tm); - snprintf(&buf[strlen(buf)], 10, "%09lu", subs); + strftime(buf, sizeof(buf) - 10, "%Y-%m-%d %H:%M:%S", &tm); return buf; } @@ -1584,6 +1583,21 @@ std::vector Database::get_tags() { return out; } +std::vector Database::get_runs() const { + std::vector out; + begin_txn(); + while (sqlite3_step(imp->get_all_runs) == SQLITE_ROW) { + RunReflection run; + run.id = sqlite3_column_int(imp->get_all_runs, 0); + run.time = Time(sqlite3_column_int64(imp->get_all_runs, 1)); + run.cmdline = rip_column(imp->get_all_runs, 2); + out.emplace_back(run); + } + finish_stmt("Could not retrieve runs", imp->get_all_runs, imp->debugdb); + end_txn(); + return out; +} + std::vector> Database::get_interleaved_output(long job_id) const { std::vector> out; diff --git a/src/runtime/database.h b/src/runtime/database.h index a4663558a..615083d2b 100644 --- a/src/runtime/database.h +++ b/src/runtime/database.h @@ -60,6 +60,15 @@ struct Time { std::string as_string() const; }; +struct RunReflection { + int id; + Time time; + std::string cmdline; + RunReflection() = default; + RunReflection(int id_, int64_t time_, std::string cmdline_) + : id(id_), time(time_), cmdline(cmdline_) {} +}; + struct JobReflection { long job; bool stale; @@ -169,6 +178,8 @@ struct Database { std::vector get_edges(); std::vector get_tags(); + std::vector get_runs() const; + std::vector get_file_dependencies() const; std::vector> get_interleaved_output(long job_id) const; diff --git a/tools/wake/cli_options.h b/tools/wake/cli_options.h index 4922128aa..e9648c242 100644 --- a/tools/wake/cli_options.h +++ b/tools/wake/cli_options.h @@ -39,6 +39,7 @@ struct CommandLineOptions { int profileh; bool last_use; bool last_exe; + bool history; bool lsp; bool failed; bool script; @@ -133,6 +134,7 @@ struct CommandLineOptions { {'l', "last", GOPT_ARGUMENT_FORBIDDEN}, {0, "last-used", GOPT_ARGUMENT_FORBIDDEN}, {0, "last-executed", GOPT_ARGUMENT_FORBIDDEN}, + {0, "history", GOPT_ARGUMENT_FORBIDDEN}, {0, "lsp", GOPT_ARGUMENT_FORBIDDEN}, {'f', "failed", GOPT_ARGUMENT_FORBIDDEN}, {'s', "script", GOPT_ARGUMENT_FORBIDDEN}, @@ -192,6 +194,7 @@ struct CommandLineOptions { profileh = arg(options, "profile-heap")->count; last_use = arg(options, "last")->count || arg(options, "last-used")->count; last_exe = arg(options, "last-executed")->count; + history = arg(options, "history")->count; lsp = arg(options, "lsp")->count; failed = arg(options, "failed")->count; script = arg(options, "script")->count; diff --git a/tools/wake/describe.cpp b/tools/wake/describe.cpp index f00af1e2d..9cff70ec5 100644 --- a/tools/wake/describe.cpp +++ b/tools/wake/describe.cpp @@ -528,7 +528,7 @@ std::ostream &operator<<(std::ostream &os, const GraphNode &node) { return os << std::endl; } -JAST create_tagdag(Database &db, const std::string &tagExpr) { +void output_tagdag(Database &db, const std::string &tagExpr) { RE2 exp(tagExpr); // Pick only those tags that match the RegExp @@ -622,5 +622,5 @@ JAST create_tagdag(Database &db, const std::string &tagExpr) { } } - return out; + std::cout << out << std::endl; } diff --git a/tools/wake/describe.h b/tools/wake/describe.h index 63ff7738c..5a6d8cc1b 100644 --- a/tools/wake/describe.h +++ b/tools/wake/describe.h @@ -110,6 +110,6 @@ struct DescribePolicy { }; void describe(const std::vector &jobs, DescribePolicy policy, const Database &db); -JAST create_tagdag(Database &db, const std::string &tag); +void output_tagdag(Database &db, const std::string &tag); #endif diff --git a/tools/wake/main.cpp b/tools/wake/main.cpp index 432eab8ff..5b26f5514 100644 --- a/tools/wake/main.cpp +++ b/tools/wake/main.cpp @@ -168,15 +168,14 @@ void hide_internal_jobs(std::vector> &out) { out.push_back({"tags NOT LIKE '%inspect.visibility=hidden%'", "tags IS NULL"}); } -void inspect_database(const CommandLineOptions &clo, Database &db, const std::string &wake_cwd) { - // tagdag is technically a db inspection, but its very different from the - // rest, just handle it and exit. - if (clo.tagdag) { - JAST json = create_tagdag(db, clo.tagdag); - std::cout << json << std::endl; - return; +void query_runs(Database &db) { + const auto runs = db.get_runs(); + for (const auto run : runs) { + std::cout << run.time.as_string() << " " << run.cmdline << std::endl; } +} +void query_jobs(const CommandLineOptions &clo, Database &db) { std::vector> collect_ands = {}; std::vector> collect_input_ands = {}; std::vector> collect_output_ands = {}; @@ -228,6 +227,18 @@ void inspect_database(const CommandLineOptions &clo, Database &db, const std::st describe(matching_jobs, get_describe_policy(clo), db); } +void inspect_database(const CommandLineOptions &clo, Database &db) { + // tagdag and history are db inspection queries, but are very different from the + // rest of the queries which operate on the jobs table. + if (clo.tagdag) { + output_tagdag(db, clo.tagdag); + } else if (clo.history) { + query_runs(db); + } else { + query_jobs(clo, db); + } +} + } // namespace void print_help(const char *argv0) { @@ -268,6 +279,7 @@ void print_help(const char *argv0) { << " --last -l See --last-used" << std::endl << " --last-used Capture all jobs used by last build. Regardless of cache" << std::endl << " --last-executed Capture all jobs executed by the last build. Skips cache" << std::endl + << " --history Report the cmndline history of all wake commands recorded" << std::endl << " --failed -f Capture jobs which failed last build" << std::endl << " --tag KEY=VAL Capture jobs which are tagged, matching KEY and VAL globs" << std::endl << " --canceled Capture jobs which were canceled in the last build" << std::endl @@ -420,7 +432,7 @@ int main(int argc, char **argv) { bool is_db_inspect_capture = !clo.job_ids.empty() || !clo.output_files.empty() || !clo.input_files.empty() || !clo.labels.empty() || !clo.tags.empty() || clo.last_use || clo.last_exe || clo.failed || - clo.tagdag || clo.canceled; + clo.tagdag || clo.canceled || clo.history; // DescribePolicy::human() is the default and doesn't have a flag. // DescribePolicy::debug() is overloaded and can't be marked as a db flag @@ -639,7 +651,7 @@ int main(int argc, char **argv) { } if (is_db_inspection) { - inspect_database(clo, db, wake_cwd); + inspect_database(clo, db); return 0; }