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

llnl / dftracer-utils / 27171677342

08 Jun 2026 10:43PM UTC coverage: 51.99% (+0.05%) from 51.937%
27171677342

Pull #77

github

web-flow
Merge 3a1432eec into 8045f0be3
Pull Request #77: chore: bump version to 0.0.10

36972 of 92663 branches covered (39.9%)

Branch coverage included in aggregate %.

33405 of 42703 relevant lines covered (78.23%)

20411.31 hits per line

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

52.17
/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/internal/utils.h>
7
#include <dftracer/utils/utilities/composites/dft/statistics/statistics_aggregator_utility.h>
8
#include <dftracer/utils/utilities/composites/dft/statistics/statistics_query_utility.h>
9

10
#include <string>
11

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

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

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

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

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

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

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

64
    return 0;
16✔
65
}
8✔
66

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

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

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

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

115
    Py_BEGIN_ALLOW_THREADS try {
16!
116
        Runtime *rt = get_runtime(self);
16!
117

118
        StatisticsAggregatorInput agg_input;
16✔
119
        agg_input.file_path = file_path_str;
16!
120
        agg_input.index_dir = index_dir_str;
16!
121
        agg_input.index_path = dftracer::utils::utilities::composites::dft::
8!
122
            internal::determine_index_path(file_path_str, index_dir_str);
8!
123

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

131
        StatisticsQueryInput query_input;
16!
132
        query_input.stats = std::move(stats);
16✔
133
        query_input.query_type = qt_copy;
16✔
134
        query_input.top_n = top_n_copy;
16✔
135

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

147
        if (!error_msg.empty()) {
16!
148
        PyErr_SetString(PyExc_RuntimeError, error_msg.c_str());
×
149
        return NULL;
×
150
    }
151

152
    PyObject *results_list =
8✔
153
        PyList_New(static_cast<Py_ssize_t>(output.results.size()));
16!
154
    if (!results_list) return NULL;
16✔
155

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

169
    PyObject *d = PyDict_New();
16!
170
    if (!d) {
16!
171
        Py_DECREF(results_list);
×
172
        return NULL;
×
173
    }
174

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

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

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

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

222
    if (PyDict_SetItemString(d, "results", results_list) < 0) {
16!
223
        Py_DECREF(results_list);
×
224
        Py_DECREF(d);
×
225
        return NULL;
×
226
    }
227
    Py_DECREF(results_list);
8!
228

229
#undef SET_STR
230
#undef SET_ULL
231
#undef SET_DBL
232

233
    return d;
16✔
234
}
16✔
235

236
static PyObject *StatisticsQuery_call(PyObject *self, PyObject *args,
2✔
237
                                      PyObject *kwds) {
238
    return StatisticsQuery_query((StatisticsQueryObject *)self, args, kwds);
2✔
239
}
240

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

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

307
int init_statistics_query(PyObject *m) {
2✔
308
    if (PyType_Ready(&StatisticsQueryUtilityType) < 0) return -1;
2✔
309

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

318
    return 0;
2✔
319
}
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