summaryrefslogtreecommitdiff
path: root/debuginfod/debuginfod.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'debuginfod/debuginfod.cxx')
-rw-r--r--debuginfod/debuginfod.cxx200
1 files changed, 119 insertions, 81 deletions
diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx
index b9cf15ed..105c3908 100644
--- a/debuginfod/debuginfod.cxx
+++ b/debuginfod/debuginfod.cxx
@@ -176,7 +176,7 @@ static const char DEBUGINFOD_SQLITE_DDL[] =
" foreign key (buildid) references " BUILDIDS "_buildids(id) on update cascade on delete cascade,\n"
" primary key (buildid, file, mtime)\n"
" ) " WITHOUT_ROWID ";\n"
- // Index for faster delete by file identifier
+ // Index for faster delete by file identifier and metadata searches
"create index if not exists " BUILDIDS "_f_de_idx on " BUILDIDS "_f_de (file, mtime);\n"
"create table if not exists " BUILDIDS "_f_s (\n"
" buildid integer not null,\n"
@@ -202,6 +202,8 @@ static const char DEBUGINFOD_SQLITE_DDL[] =
" ) " WITHOUT_ROWID ";\n"
// Index for faster delete by archive file identifier
"create index if not exists " BUILDIDS "_r_de_idx on " BUILDIDS "_r_de (file, mtime);\n"
+ // Index for metadata searches
+ "create index if not exists " BUILDIDS "_r_de_idx2 on " BUILDIDS "_r_de (content);\n"
"create table if not exists " BUILDIDS "_r_sref (\n" // outgoing dwarf sourcefile references from rpm
" buildid integer not null,\n"
" artifactsrc integer not null,\n"
@@ -389,6 +391,9 @@ static const struct argp_option options[] =
{ "passive", ARGP_KEY_PASSIVE, NULL, 0, "Do not scan or groom, read-only database.", 0 },
#define ARGP_KEY_DISABLE_SOURCE_SCAN 0x1009
{ "disable-source-scan", ARGP_KEY_DISABLE_SOURCE_SCAN, NULL, 0, "Do not scan dwarf source info.", 0 },
+#define ARGP_KEY_METADATA_MAXTIME 0x100A
+ { "metadata-maxtime", ARGP_KEY_METADATA_MAXTIME, "SECONDS", 0,
+ "Number of seconds to limit metadata query run time, 0=unlimited.", 0 },
{ NULL, 0, NULL, 0, NULL, 0 },
};
@@ -441,6 +446,8 @@ static unsigned forwarded_ttl_limit = 8;
static bool scan_source_info = true;
static string tmpdir;
static bool passive_p = false;
+static unsigned metadata_maxtime_s = 5;
+
static void set_metric(const string& key, double value);
// static void inc_metric(const string& key);
@@ -642,6 +649,9 @@ parse_opt (int key, char *arg,
case ARGP_KEY_DISABLE_SOURCE_SCAN:
scan_source_info = false;
break;
+ case ARGP_KEY_METADATA_MAXTIME:
+ metadata_maxtime_s = (unsigned) atoi(arg);
+ break;
// case 'h': argp_state_help (state, stderr, ARGP_HELP_LONG|ARGP_HELP_EXIT_OK);
default: return ARGP_ERR_UNKNOWN;
}
@@ -2277,74 +2287,101 @@ handle_metrics (off_t* size)
return r;
}
+
+#ifdef HAVE_JSON_C
static struct MHD_Response*
handle_metadata (MHD_Connection* conn,
- string path, off_t* size)
+ string key, string value, off_t* size)
{
MHD_Response* r;
- (void) conn;
- (void) path;
- (void) size;
- #ifdef HAVE_JSON_C
sqlite3 *thisdb = dbq;
- json_object *metadata = json_object_new_array();
// Query locally for matching e, d and s files
- if(metadata){
- sqlite_ps *pp = new sqlite_ps (thisdb, "mhd-query-m",
- "select * from \n"
- "(\n"
- "select \"e\" as atype, mtime, buildid, sourcetype, source0, source1, null as artifactsrc, null as source0ref "
- "from " BUILDIDS "_query_e "
- "union all \n"
- "select \"d\" as atype, mtime, buildid, sourcetype, source0, source1, null as artifactsrc, null as source0ref "
- "from " BUILDIDS "_query_d "
- "union all \n"
- "select \"s\" as atype, mtime, buildid, sourcetype, source0, source1, artifactsrc, source0ref "
- "from " BUILDIDS "_query_s "
- "\n"
- ")\n"
- "where source1 glob ? ");
-
- pp->reset();
- pp->bind(1, path);
-
- unique_ptr<sqlite_ps> ps_closer(pp); // release pp if exception or return
-
- // consume all the rows
- int rc;
- while (SQLITE_DONE != (rc = pp->step()))
+
+ string op;
+ if (key == "glob")
+ op = "glob";
+ else if (key == "file")
+ op = "=";
+ else
+ throw reportable_exception("/metadata webapi error, unsupported key");
+
+ string sql = string(
+ // explicit query r_de and f_de once here, rather than the query_d and query_e
+ // separately, because they scan the same tables, so we'd double the work
+ "select d1.executable_p, d1.debuginfo_p, 0 as source_p, b1.hex, f1.name as file "
+ "from " BUILDIDS "_r_de d1, " BUILDIDS "_files f1, " BUILDIDS "_buildids b1 "
+ "where f1.id = d1.content and d1.buildid = b1.id and f1.name " + op + " ? "
+ "union all \n"
+ "select d2.executable_p, d2.debuginfo_p, 0, b2.hex, f2.name "
+ "from " BUILDIDS "_f_de d2, " BUILDIDS "_files f2, " BUILDIDS "_buildids b2 "
+ "where f2.id = d2.file and d2.buildid = b2.id and f2.name " + op + " ? "
+ "union all \n"
+ // delegate to query_s for this one
+ "select 0, 0, 1, q.buildid, q.artifactsrc "
+ "from " BUILDIDS "_query_s as q "
+ "where q.artifactsrc " + op + " ? ");
+
+ sqlite_ps *pp = new sqlite_ps (thisdb, "mhd-query-meta-glob", sql);
+ pp->reset();
+ pp->bind(1, value);
+ pp->bind(2, value);
+ pp->bind(3, value);
+ unique_ptr<sqlite_ps> ps_closer(pp); // release pp if exception or return
+
+ json_object *metadata = json_object_new_array();
+ if (!metadata)
+ throw libc_exception(ENOMEM, "json allocation");
+
+ // consume all the rows
+ struct timespec ts_start;
+ clock_gettime (CLOCK_MONOTONIC, &ts_start);
+
+ int rc;
+ while (SQLITE_DONE != (rc = pp->step()))
{
+ // break out of loop if we have searched too long
+ struct timespec ts_end;
+ clock_gettime (CLOCK_MONOTONIC, &ts_end);
+ double deltas = (ts_end.tv_sec - ts_start.tv_sec) + (ts_end.tv_nsec - ts_start.tv_nsec)/1.e9;
+ if (metadata_maxtime_s > 0 && deltas > metadata_maxtime_s)
+ break; // NB: no particular signal is given to the client about incompleteness
+
if (rc != SQLITE_ROW) throw sqlite_exception(rc, "step");
- auto get_column_str = [pp](int idx) { return string((const char*) sqlite3_column_text (*pp, idx) ?: ""); };
-
- string atype = get_column_str(0);
- string mtime = to_string(sqlite3_column_int64 (*pp, 1));
- string buildid = get_column_str(2);
- string stype = get_column_str(3);
- string source0 = get_column_str(4);
- string source1 = get_column_str(5);
- string artifactsrc = get_column_str(6);
- string source0ref = get_column_str(7);
-
- json_object *entry = json_object_new_object();
- auto add_entry_metadata = [entry](const char* key, string value) { if(value != "") json_object_object_add(entry, key, json_object_new_string(value.c_str())); };
-
- add_entry_metadata("atype", atype);
- add_entry_metadata("mtime", mtime);
- add_entry_metadata("buildid", buildid);
- add_entry_metadata("stype", stype);
- add_entry_metadata("source0", source0);
- add_entry_metadata("source1", source1);
- add_entry_metadata("artifactsrc", artifactsrc);
- add_entry_metadata("source0ref", source0ref);
-
- json_object_array_add(metadata, json_object_get(entry)); // Increase ref count to switch its ownership
- json_object_put(entry);
+ int m_executable_p = sqlite3_column_int (*pp, 0);
+ int m_debuginfo_p = sqlite3_column_int (*pp, 1);
+ int m_source_p = sqlite3_column_int (*pp, 2);
+ string m_buildid = (const char*) sqlite3_column_text (*pp, 3) ?: ""; // should always be non-null
+ string m_file = (const char*) sqlite3_column_text (*pp, 4) ?: "";
+
+ auto add_metadata = [metadata, m_buildid, m_file](const string& type) {
+ json_object* entry = json_object_new_object();
+ if (NULL == entry) throw libc_exception (ENOMEM, "cannot allocate json");
+ defer_dtor<json_object*,int> entry_d(entry, json_object_put);
+
+ auto add_entry_metadata = [entry](const char* k, string v) {
+ json_object* s;
+ if(v != "") {
+ s = json_object_new_string(v.c_str());
+ if (NULL == s) throw libc_exception (ENOMEM, "cannot allocate json");
+ json_object_object_add(entry, k, s);
+ }
+ };
+
+ add_entry_metadata("type", type.c_str());
+ add_entry_metadata("buildid", m_buildid);
+ add_entry_metadata("file", m_file);
+ json_object_array_add(metadata, json_object_get(entry)); // Increase ref count to switch its ownership
+ };
+
+ if (m_executable_p) add_metadata("executable");
+ if (m_debuginfo_p) add_metadata("debuginfo");
+ if (m_source_p) add_metadata("source");
+
}
- pp->reset();
- }
+ pp->reset();
+
// Query upstream as well
debuginfod_client *client = debuginfod_pool_begin();
if (metadata && client != NULL)
@@ -2352,15 +2389,17 @@ handle_metadata (MHD_Connection* conn,
add_client_federation_headers(client, conn);
char * upstream_metadata;
- if(0 == debuginfod_find_metadata(client, path.c_str(), &upstream_metadata)){
+ if (0 == debuginfod_find_metadata(client, key.c_str(), value.c_str(), &upstream_metadata)) {
json_object *upstream_metadata_json = json_tokener_parse(upstream_metadata);
if(NULL != upstream_metadata_json)
- for (int i = 0, n = json_object_array_length(upstream_metadata_json); i < n; i++) {
+ {
+ for (int i = 0, n = json_object_array_length(upstream_metadata_json); i < n; i++) {
json_object *entry = json_object_array_get_idx(upstream_metadata_json, i);
json_object_get(entry); // increment reference count
json_object_array_add(metadata, entry);
+ }
+ json_object_put(upstream_metadata_json);
}
- json_object_put(upstream_metadata_json);
free(upstream_metadata);
}
debuginfod_pool_end (client);
@@ -2368,18 +2407,19 @@ handle_metadata (MHD_Connection* conn,
const char* metadata_str = (metadata != NULL) ?
json_object_to_json_string(metadata) : "[ ]" ;
+ if (! metadata_str)
+ throw libc_exception (ENOMEM, "cannot allocate json");
r = MHD_create_response_from_buffer (strlen(metadata_str),
(void*) metadata_str,
MHD_RESPMEM_MUST_COPY);
*size = strlen(metadata_str);
json_object_put(metadata);
- #else
- throw reportable_exception("webapi error, metadata querying not supported by server");
- #endif
- if(r)
+ if (r)
add_mhd_response_header(r, "Content-Type", "application/json");
return r;
}
+#endif
+
static struct MHD_Response*
handle_root (off_t* size)
@@ -2515,23 +2555,20 @@ handler_cb (void * /*cls*/,
inc_metric("http_requests_total", "type", artifacttype);
r = handle_metrics(& http_size);
}
+#ifdef HAVE_JSON_C
else if (url1 == "/metadata")
{
tmp_inc_metric m ("thread_busy", "role", "http-metadata");
-
- // At the moment only glob path querying is supported, but leave room for
- // future expansion
- const char *key = "glob";
-
- const char* path = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, key);
- if (NULL == path)
- throw reportable_exception("/metadata webapi error, need glob");
+ const char* key = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "key");
+ const char* value = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "value");
+ if (NULL == value || NULL == key)
+ throw reportable_exception("/metadata webapi error, need key and value");
artifacttype = "metadata";
inc_metric("http_requests_total", "type", artifacttype);
- r = handle_metadata(connection, path, &http_size);
-
+ r = handle_metadata(connection, key, value, &http_size);
}
+#endif
else if (url1 == "/")
{
artifacttype = "/";
@@ -3819,12 +3856,13 @@ void groom()
if (interrupted) return;
// NB: "vacuum" is too heavy for even daily runs: it rewrites the entire db, so is done as maxigroom -G
- sqlite_ps g1 (db, "incremental vacuum", "pragma incremental_vacuum");
- g1.reset().step_ok_done();
- sqlite_ps g2 (db, "optimize", "pragma optimize");
- g2.reset().step_ok_done();
- sqlite_ps g3 (db, "wal checkpoint", "pragma wal_checkpoint=truncate");
- g3.reset().step_ok_done();
+ { sqlite_ps g (db, "incremental vacuum", "pragma incremental_vacuum"); g.reset().step_ok_done(); }
+ // https://www.sqlite.org/lang_analyze.html#approx
+ { sqlite_ps g (db, "analyze setup", "pragma analysis_limit = 1000;\n"); g.reset().step_ok_done(); }
+ { sqlite_ps g (db, "analyze", "analyze"); g.reset().step_ok_done(); }
+ { sqlite_ps g (db, "analyze reload", "analyze sqlite_schema"); g.reset().step_ok_done(); }
+ { sqlite_ps g (db, "optimize", "pragma optimize"); g.reset().step_ok_done(); }
+ { sqlite_ps g (db, "wal checkpoint", "pragma wal_checkpoint=truncate"); g.reset().step_ok_done(); }
database_stats_report();