• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

llnl / dftracer-utils / 23531027933

25 Mar 2026 08:05AM UTC coverage: 48.592% (-1.5%) from 50.098%
23531027933

Pull #57

github

web-flow
Merge d1070e289 into 38f9f3616
Pull Request #57: feat(comparator): add pairwise traces comparator

18900 of 49456 branches covered (38.22%)

Branch coverage included in aggregate %.

1604 of 1954 new or added lines in 25 files covered. (82.09%)

3407 existing lines in 135 files now uncovered.

18487 of 27485 relevant lines covered (67.26%)

240991.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.55
/src/dftracer/utils/python/utilities/statistics_query.cpp
1
#define PY_SSIZE_T_CLEAN
2
#include <dftracer/utils/core/coro/task.h>
3
#include <dftracer/utils/core/runtime.h>
4
#include <dftracer/utils/python/runtime.h>
5
#include <dftracer/utils/python/utilities/statistics_query.h>
6
#include <dftracer/utils/utilities/composites/dft/statistics/statistics_aggregator_utility.h>
7
#include <dftracer/utils/utilities/composites/dft/statistics/statistics_query_utility.h>
8

9
#include <string>
10

11
using dftracer::utils::Runtime;
12
using dftracer::utils::coro::CoroTask;
13
using namespace dftracer::utils::utilities::composites::dft::statistics;
14

15
static Runtime *get_runtime(StatisticsQueryObject *self) {
6✔
16
    if (self->runtime_obj)
6!
17
        return ((RuntimeObject *)self->runtime_obj)->runtime.get();
×
18
    return get_default_runtime();
6✔
19
}
6✔
20

21
static void StatisticsQuery_dealloc(StatisticsQueryObject *self) {
6✔
22
    Py_XDECREF(self->runtime_obj);
6✔
23
    Py_TYPE(self)->tp_free((PyObject *)self);
6✔
24
}
6✔
25

26
static PyObject *StatisticsQuery_new(PyTypeObject *type, PyObject *args,
6✔
27
                                     PyObject *kwds) {
28
    StatisticsQueryObject *self =
6✔
29
        (StatisticsQueryObject *)type->tp_alloc(type, 0);
6✔
30
    if (self) {
6!
31
        self->runtime_obj = NULL;
6✔
32
    }
6✔
33
    return (PyObject *)self;
6✔
34
}
35

36
static int StatisticsQuery_init(StatisticsQueryObject *self, PyObject *args,
6✔
37
                                PyObject *kwds) {
38
    static const char *kwlist[] = {"runtime", NULL};
39
    PyObject *runtime_arg = NULL;
6✔
40

41
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", (char **)kwlist,
6!
42
                                     &runtime_arg)) {
43
        return -1;
×
44
    }
45

46
    if (runtime_arg && runtime_arg != Py_None) {
6!
47
        if (PyObject_TypeCheck(runtime_arg, &RuntimeType)) {
×
48
            Py_INCREF(runtime_arg);
×
49
            self->runtime_obj = runtime_arg;
×
UNCOV
50
        } else {
×
51
            PyObject *native = PyObject_GetAttrString(runtime_arg, "_native");
×
52
            if (native && PyObject_TypeCheck(native, &RuntimeType)) {
×
53
                self->runtime_obj = native;
×
UNCOV
54
            } else {
×
55
                Py_XDECREF(native);
×
56
                PyErr_SetString(PyExc_TypeError,
×
57
                                "runtime must be a Runtime instance or None");
58
                return -1;
×
59
            }
60
        }
UNCOV
61
    }
×
62

63
    return 0;
6✔
64
}
6✔
65

66
static PyObject *StatisticsQuery_query(StatisticsQueryObject *self,
6✔
67
                                       PyObject *args, PyObject *kwds) {
68
    static const char *kwlist[] = {"file_path", "query_type", "top_n",
69
                                   "index_dir", NULL};
70
    const char *file_path;
71
    const char *query_type_str = "summary";
6✔
72
    Py_ssize_t top_n = 10;
6✔
73
    const char *index_dir = "";
6✔
74

75
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|sns", (char **)kwlist,
6!
76
                                     &file_path, &query_type_str, &top_n,
77
                                     &index_dir)) {
78
        return NULL;
×
79
    }
80

81
    StatisticsQueryType qt;
82
    if (strcmp(query_type_str, "summary") == 0) {
6✔
83
        qt = StatisticsQueryType::SUMMARY;
2✔
84
    } else if (strcmp(query_type_str, "categories") == 0) {
6✔
85
        qt = StatisticsQueryType::CATEGORIES;
1✔
86
    } else if (strcmp(query_type_str, "names") == 0) {
4✔
87
        qt = StatisticsQueryType::NAMES;
1✔
88
    } else if (strcmp(query_type_str, "pid_tids") == 0) {
3!
89
        qt = StatisticsQueryType::PID_TIDS;
×
90
    } else if (strcmp(query_type_str, "time_range") == 0) {
2!
91
        qt = StatisticsQueryType::TIME_RANGE;
×
92
    } else if (strcmp(query_type_str, "duration_stats") == 0) {
2✔
93
        qt = StatisticsQueryType::DURATION_STATS;
1✔
94
    } else if (strcmp(query_type_str, "top_n_names") == 0) {
2!
95
        qt = StatisticsQueryType::TOP_N_NAMES;
1✔
96
    } else if (strcmp(query_type_str, "top_n_categories") == 0) {
1!
97
        qt = StatisticsQueryType::TOP_N_CATEGORIES;
×
98
    } else if (strcmp(query_type_str, "detailed") == 0) {
×
99
        qt = StatisticsQueryType::DETAILED;
×
UNCOV
100
    } else {
×
101
        PyErr_Format(PyExc_ValueError, "unknown query_type: '%s'",
×
UNCOV
102
                     query_type_str);
×
103
        return NULL;
×
104
    }
105

106
    std::string file_path_str(file_path);
6✔
107
    std::string index_dir_str(index_dir);
6!
108
    std::string error_msg;
6✔
109
    TraceStatistics stats;
6!
110
    StatisticsQueryOutput output;
6✔
111
    auto qt_copy = qt;
6✔
112
    auto top_n_copy = static_cast<std::uint64_t>(top_n);
6✔
113

114
    Py_BEGIN_ALLOW_THREADS try {
6!
115
        Runtime *rt = get_runtime(self);
6!
116

117
        StatisticsAggregatorInput agg_input;
6✔
118
        agg_input.file_path = file_path_str;
6!
119
        agg_input.index_dir = index_dir_str;
6!
120
        agg_input.idx_path = file_path_str + ".idx";
6!
121

122
        auto *stats_p = &stats;
6✔
123
        auto agg_task = [stats_p, agg_input]() -> CoroTask<void> {
48!
124
            StatisticsAggregatorUtility util;
18!
125
            *stats_p = co_await util.process(agg_input);
24!
126
        };
18!
127
        rt->submit(agg_task(), "stats-agg").get();
6!
128

129
        StatisticsQueryInput query_input;
6!
130
        query_input.stats = std::move(stats);
6✔
131
        query_input.query_type = qt_copy;
6✔
132
        query_input.top_n = top_n_copy;
6✔
133

134
        auto *out_p = &output;
6✔
135
        auto query_task = [out_p, query_input]() -> CoroTask<void> {
48!
136
            StatisticsQueryUtility util;
18!
137
            *out_p = co_await util.process(query_input);
24!
138
        };
18!
139
        rt->submit(query_task(), "stats-query").get();
6!
140
    } catch (const std::exception &e) {
6!
141
        error_msg = e.what();
×
142
    }
×
143
    Py_END_ALLOW_THREADS
6!
144

145
        if (!error_msg.empty()) {
6!
146
        PyErr_SetString(PyExc_RuntimeError, error_msg.c_str());
×
147
        return NULL;
×
148
    }
149

150
    PyObject *results_list =
6✔
151
        PyList_New(static_cast<Py_ssize_t>(output.results.size()));
6!
152
    if (!results_list) return NULL;
6!
153

154
    for (std::size_t i = 0; i < output.results.size(); ++i) {
61✔
155
        PyObject *tup = PyTuple_New(2);
55!
156
        if (!tup) {
55!
UNCOV
157
            Py_DECREF(results_list);
×
158
            return NULL;
×
159
        }
160
        PyTuple_SET_ITEM(tup, 0,
55!
161
                         PyUnicode_FromString(output.results[i].first.c_str()));
162
        PyTuple_SET_ITEM(tup, 1,
55!
163
                         PyLong_FromUnsignedLongLong(output.results[i].second));
164
        PyList_SET_ITEM(results_list, static_cast<Py_ssize_t>(i), tup);
55!
165
    }
55✔
166

167
    PyObject *d = PyDict_New();
6!
168
    if (!d) {
6!
UNCOV
169
        Py_DECREF(results_list);
×
170
        return NULL;
×
171
    }
172

173
#define SET_STR(k, v)                                    \
174
    do {                                                 \
175
        PyObject *_v = PyUnicode_FromString(v);          \
176
        if (!_v || PyDict_SetItemString(d, k, _v) < 0) { \
177
            Py_XDECREF(_v);                              \
178
            Py_DECREF(results_list);                     \
179
            Py_DECREF(d);                                \
180
            return NULL;                                 \
181
        }                                                \
182
        Py_DECREF(_v);                                   \
183
    } while (0)
184

185
#define SET_ULL(k, v)                                    \
186
    do {                                                 \
187
        PyObject *_v = PyLong_FromUnsignedLongLong(v);   \
188
        if (!_v || PyDict_SetItemString(d, k, _v) < 0) { \
189
            Py_XDECREF(_v);                              \
190
            Py_DECREF(results_list);                     \
191
            Py_DECREF(d);                                \
192
            return NULL;                                 \
193
        }                                                \
194
        Py_DECREF(_v);                                   \
195
    } while (0)
196

197
#define SET_DBL(k, v)                                    \
198
    do {                                                 \
199
        PyObject *_v = PyFloat_FromDouble(v);            \
200
        if (!_v || PyDict_SetItemString(d, k, _v) < 0) { \
201
            Py_XDECREF(_v);                              \
202
            Py_DECREF(results_list);                     \
203
            Py_DECREF(d);                                \
204
            return NULL;                                 \
205
        }                                                \
206
        Py_DECREF(_v);                                   \
207
    } while (0)
208

209
    SET_STR("query_type", output.query_type_name.c_str());
6!
210
    SET_ULL("total_events", output.total_events);
6!
211
    SET_ULL("min_timestamp_us", output.min_timestamp_us);
6!
212
    SET_ULL("max_timestamp_us", output.max_timestamp_us);
6!
213
    SET_DBL("time_span_seconds", output.time_span_seconds);
6!
214
    SET_ULL("duration_count", output.duration_count);
6!
215
    SET_DBL("duration_mean_us", output.duration_mean_us);
6!
216
    SET_DBL("duration_stddev_us", output.duration_stddev_us);
6!
217
    SET_ULL("duration_min_us", output.duration_min_us);
6!
218
    SET_ULL("duration_max_us", output.duration_max_us);
6!
219

220
    if (PyDict_SetItemString(d, "results", results_list) < 0) {
6!
UNCOV
221
        Py_DECREF(results_list);
×
UNCOV
222
        Py_DECREF(d);
×
223
        return NULL;
×
224
    }
225
    Py_DECREF(results_list);
6!
226

227
#undef SET_STR
228
#undef SET_ULL
229
#undef SET_DBL
230

231
    return d;
6✔
232
}
6✔
233

234
static PyObject *StatisticsQuery_call(PyObject *self, PyObject *args,
1✔
235
                                      PyObject *kwds) {
236
    return StatisticsQuery_query((StatisticsQueryObject *)self, args, kwds);
1✔
237
}
238

239
static PyMethodDef StatisticsQuery_methods[] = {
240
    {"process", (PyCFunction)StatisticsQuery_query,
241
     METH_VARARGS | METH_KEYWORDS,
242
     "process(file_path, query_type='summary', top_n=10, index_dir='')\n"
243
     "--\n"
244
     "\n"
245
     "Query statistics from an indexed trace file.\n"
246
     "\n"
247
     "Args:\n"
248
     "    file_path (str): Path to the trace file.\n"
249
     "    query_type (str): Query type (default 'summary'). One of\n"
250
     "        'summary', 'categories', 'names', 'pid_tids',\n"
251
     "        'time_range', 'duration_stats', 'top_n_names',\n"
252
     "        'top_n_categories', 'detailed'.\n"
253
     "    top_n (int): Top results for ranked queries (default 10).\n"
254
     "    index_dir (str): Directory for index sidecars (default '').\n"
255
     "\n"
256
     "Returns:\n"
257
     "    dict: Query results.\n"},
258
    {NULL}};
259

260
PyTypeObject StatisticsQueryUtilityType = {
261
    PyVarObject_HEAD_INIT(
262
        NULL, 0) "dftracer_utils_ext.StatisticsQueryUtility", /* tp_name */
263
    sizeof(StatisticsQueryObject),                            /* tp_basicsize */
264
    0,                                                        /* tp_itemsize */
265
    (destructor)StatisticsQuery_dealloc,                      /* tp_dealloc */
266
    0,                                        /* tp_vectorcall_offset */
267
    0,                                        /* tp_getattr */
268
    0,                                        /* tp_setattr */
269
    0,                                        /* tp_as_async */
270
    0,                                        /* tp_repr */
271
    0,                                        /* tp_as_number */
272
    0,                                        /* tp_as_sequence */
273
    0,                                        /* tp_as_mapping */
274
    0,                                        /* tp_hash */
275
    StatisticsQuery_call,                     /* tp_call */
276
    0,                                        /* tp_str */
277
    0,                                        /* tp_getattro */
278
    0,                                        /* tp_setattro */
279
    0,                                        /* tp_as_buffer */
280
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
281
    "StatisticsQueryUtility(runtime: Runtime | None = None)\n"
282
    "--\n\n"
283
    "Query pre-computed statistics from an indexed trace file.\n\n"
284
    "Args:\n"
285
    "    runtime (Runtime or None): Runtime for thread pool control.\n",
286
    0,                              /* tp_traverse */
287
    0,                              /* tp_clear */
288
    0,                              /* tp_richcompare */
289
    0,                              /* tp_weaklistoffset */
290
    0,                              /* tp_iter */
291
    0,                              /* tp_iternext */
292
    StatisticsQuery_methods,        /* tp_methods */
293
    0,                              /* tp_members */
294
    0,                              /* tp_getset */
295
    0,                              /* tp_base */
296
    0,                              /* tp_dict */
297
    0,                              /* tp_descr_get */
298
    0,                              /* tp_descr_set */
299
    0,                              /* tp_dictoffset */
300
    (initproc)StatisticsQuery_init, /* tp_init */
301
    0,                              /* tp_alloc */
302
    StatisticsQuery_new,            /* tp_new */
303
};
304

305
int init_statistics_query(PyObject *m) {
1✔
306
    if (PyType_Ready(&StatisticsQueryUtilityType) < 0) return -1;
1!
307

308
    Py_INCREF(&StatisticsQueryUtilityType);
1✔
309
    if (PyModule_AddObject(m, "StatisticsQueryUtility",
2!
310
                           (PyObject *)&StatisticsQueryUtilityType) < 0) {
1✔
UNCOV
311
        Py_DECREF(&StatisticsQueryUtilityType);
×
UNCOV
312
        Py_DECREF(m);
×
313
        return -1;
×
314
    }
315

316
    return 0;
1✔
317
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc