• 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

54.2
/src/dftracer/utils/python/utilities/reorganization_planner.cpp
1
#include <dftracer/utils/core/runtime.h>
2
#include <dftracer/utils/python/runtime.h>
3
#include <dftracer/utils/python/utilities/reorganization_planner.h>
4
#include <dftracer/utils/utilities/composites/dft/reorganize/reorganization_planner.h>
5

6
#include <string>
7
#include <vector>
8

9
using dftracer::utils::Runtime;
10
using namespace dftracer::utils::utilities::composites::dft::reorganize;
11

12
static Runtime *get_runtime(ReorganizationPlannerObject *self) {
2✔
13
    if (self->runtime_obj)
2!
14
        return ((RuntimeObject *)self->runtime_obj)->runtime.get();
×
15
    return get_default_runtime();
2✔
16
}
2✔
17

18
static void ReorganizationPlanner_dealloc(ReorganizationPlannerObject *self) {
2✔
19
    Py_XDECREF(self->runtime_obj);
2✔
20
    Py_TYPE(self)->tp_free((PyObject *)self);
2✔
21
}
2✔
22

23
static PyObject *ReorganizationPlanner_new(PyTypeObject *type, PyObject *args,
2✔
24
                                           PyObject *kwds) {
25
    ReorganizationPlannerObject *self;
26
    self = (ReorganizationPlannerObject *)type->tp_alloc(type, 0);
2✔
27
    if (self != NULL) {
2!
28
        self->runtime_obj = NULL;
2✔
29
    }
2✔
30
    return (PyObject *)self;
2✔
31
}
32

33
static int ReorganizationPlanner_init(ReorganizationPlannerObject *self,
2✔
34
                                      PyObject *args, PyObject *kwds) {
35
    static const char *kwlist[] = {"runtime", NULL};
36
    PyObject *runtime_arg = NULL;
2✔
37

38
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", (char **)kwlist,
2!
39
                                     &runtime_arg)) {
40
        return -1;
×
41
    }
42

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

60
    return 0;
2✔
61
}
2✔
62

63
static PyObject *ReorganizationPlanner_plan(ReorganizationPlannerObject *self,
2✔
64
                                            PyObject *args, PyObject *kwds) {
65
    using dftracer::utils::coro::CoroTask;
66

67
    static const char *kwlist[] = {"source_files", "groups", "index_dir", NULL};
68
    PyObject *source_files_obj;
69
    PyObject *groups_obj = Py_None;
2✔
70
    const char *index_dir = "";
2✔
71

72
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Os", (char **)kwlist,
2!
73
                                     &source_files_obj, &groups_obj,
74
                                     &index_dir))
75
        return NULL;
×
76

77
    if (!PyList_Check(source_files_obj)) {
2!
78
        PyErr_SetString(PyExc_TypeError, "source_files must be a list");
×
79
        return NULL;
×
80
    }
81

82
    Py_ssize_t nfiles = PyList_Size(source_files_obj);
2✔
83
    std::vector<std::string> files;
2✔
84
    files.reserve(static_cast<std::size_t>(nfiles));
2!
85
    for (Py_ssize_t i = 0; i < nfiles; i++) {
4✔
86
        PyObject *item = PyList_GetItem(source_files_obj, i);
2!
87
        const char *s = PyUnicode_AsUTF8(item);
2!
88
        if (!s) return NULL;
2!
89
        files.emplace_back(s);
2!
90
    }
2✔
91

92
    std::vector<PredicateGroup> groups;
2✔
93
    if (groups_obj && groups_obj != Py_None) {
2!
94
        if (!PyList_Check(groups_obj)) {
2!
95
            PyErr_SetString(PyExc_TypeError,
×
96
                            "groups must be a list of dicts or None");
97
            return NULL;
×
98
        }
99
        Py_ssize_t n = PyList_Size(groups_obj);
2!
100
        groups.reserve(static_cast<std::size_t>(n));
2!
101
        for (Py_ssize_t i = 0; i < n; i++) {
4✔
102
            PyObject *item = PyList_GetItem(groups_obj, i);
2!
103
            PredicateGroup g;
2✔
104
            PyObject *name = PyDict_GetItemString(item, "name");
2!
105
            PyObject *pred = PyDict_GetItemString(item, "query");
2!
106
            if (name) {
2!
107
                const char *ns = PyUnicode_AsUTF8(name);
2!
108
                if (!ns) return NULL;
2!
109
                g.name = ns;
2!
110
            }
2✔
111
            if (pred) {
2!
112
                const char *ps = PyUnicode_AsUTF8(pred);
×
113
                if (!ps) return NULL;
×
114
                g.query = ps;
×
UNCOV
115
            }
×
116
            groups.push_back(std::move(g));
2!
117
        }
2!
118
    }
2✔
119

120
    ReorganizationPlannerInput input;
2✔
121
    input.source_files = std::move(files);
2✔
122
    input.groups = std::move(groups);
2✔
123
    input.index_dir = index_dir;
2!
124

125
    ExtractionPlan plan;
2✔
126
    auto *plan_p = &plan;
2✔
127
    ReorganizationPlannerInput input_copy = input;
2!
128
    std::string error_msg;
2✔
129

130
    Py_BEGIN_ALLOW_THREADS try {
2!
131
        Runtime *rt = get_runtime(self);
2!
132
        auto task = [plan_p, input_copy]() -> CoroTask<void> {
16!
133
            ReorganizationPlannerUtility util;
6!
134
            *plan_p = co_await util.process(input_copy);
8!
135
        };
6!
136
        rt->submit(task(), "reorganization-planner").get();
2!
137
    } catch (const std::exception &e) {
2!
138
        error_msg = e.what();
×
139
    }
×
140
    Py_END_ALLOW_THREADS
2!
141

142
        if (!error_msg.empty()) {
2!
143
        PyErr_SetString(PyExc_RuntimeError, error_msg.c_str());
×
144
        return NULL;
×
145
    }
146

147
    // groups list
148
    PyObject *py_groups =
2✔
149
        PyList_New(static_cast<Py_ssize_t>(plan.groups.size()));
2!
150
    if (!py_groups) return NULL;
2!
151
    for (std::size_t i = 0; i < plan.groups.size(); i++) {
4✔
152
        PyObject *g = PyDict_New();
2!
153
        if (!g) {
2!
UNCOV
154
            Py_DECREF(py_groups);
×
155
            return NULL;
×
156
        }
157
        PyDict_SetItemString(g, "name",
2!
158
                             PyUnicode_FromString(plan.groups[i].name.c_str()));
2!
159
        PyDict_SetItemString(
2!
160
            g, "query", PyUnicode_FromString(plan.groups[i].query.c_str()));
2!
161
        PyList_SetItem(py_groups, static_cast<Py_ssize_t>(i), g);
2!
162
    }
2✔
163

164
    // source_files list
165
    PyObject *py_sources =
2✔
166
        PyList_New(static_cast<Py_ssize_t>(plan.source_files.size()));
2!
167
    if (!py_sources) {
2!
UNCOV
168
        Py_DECREF(py_groups);
×
169
        return NULL;
×
170
    }
171
    for (std::size_t i = 0; i < plan.source_files.size(); i++) {
4✔
172
        const auto &sf = plan.source_files[i];
2✔
173
        PyObject *entry = PyDict_New();
2!
174
        if (!entry) {
2!
UNCOV
175
            Py_DECREF(py_groups);
×
UNCOV
176
            Py_DECREF(py_sources);
×
177
            return NULL;
×
178
        }
179
        PyDict_SetItemString(entry, "file_path",
2!
180
                             PyUnicode_FromString(sf.file_path.c_str()));
2!
181
        PyDict_SetItemString(entry, "idx_path",
2!
182
                             PyUnicode_FromString(sf.idx_path.c_str()));
2!
183
        PyDict_SetItemString(entry, "num_checkpoints",
2!
184
                             PyLong_FromSize_t(sf.num_checkpoints));
2!
185
        PyDict_SetItemString(entry, "uncompressed_size",
2!
186
                             PyLong_FromUnsignedLongLong(sf.uncompressed_size));
2!
187
        PyDict_SetItemString(entry, "checkpoint_size",
2!
188
                             PyLong_FromUnsignedLongLong(sf.checkpoint_size));
2!
189
        PyList_SetItem(py_sources, static_cast<Py_ssize_t>(i), entry);
2!
190
    }
2✔
191

192
    // tasks list
193
    PyObject *py_tasks = PyList_New(static_cast<Py_ssize_t>(plan.tasks.size()));
2!
194
    if (!py_tasks) {
2!
UNCOV
195
        Py_DECREF(py_groups);
×
UNCOV
196
        Py_DECREF(py_sources);
×
197
        return NULL;
×
198
    }
199
    for (std::size_t i = 0; i < plan.tasks.size(); i++) {
4✔
200
        const auto &t = plan.tasks[i];
2✔
201
        PyObject *entry = PyDict_New();
2!
202
        if (!entry) {
2!
UNCOV
203
            Py_DECREF(py_groups);
×
UNCOV
204
            Py_DECREF(py_sources);
×
UNCOV
205
            Py_DECREF(py_tasks);
×
206
            return NULL;
×
207
        }
208
        PyDict_SetItemString(entry, "source_file_idx",
2!
209
                             PyLong_FromSize_t(t.source_file_idx));
2!
210
        PyDict_SetItemString(entry, "checkpoint_idx",
2!
211
                             PyLong_FromUnsignedLongLong(t.checkpoint_idx));
2!
212
        PyDict_SetItemString(entry, "target_group",
2!
213
                             PyUnicode_FromString(t.target_group.c_str()));
2!
214
        PyDict_SetItemString(entry, "start_byte",
2!
215
                             PyLong_FromUnsignedLongLong(t.start_byte));
2!
216
        PyDict_SetItemString(entry, "end_byte",
2!
217
                             PyLong_FromUnsignedLongLong(t.end_byte));
2!
218
        PyList_SetItem(py_tasks, static_cast<Py_ssize_t>(i), entry);
2!
219
    }
2✔
220

221
    PyObject *result = PyDict_New();
2!
222
    if (!result) {
2!
UNCOV
223
        Py_DECREF(py_groups);
×
UNCOV
224
        Py_DECREF(py_sources);
×
UNCOV
225
        Py_DECREF(py_tasks);
×
226
        return NULL;
×
227
    }
228
    PyDict_SetItemString(result, "groups", py_groups);
2!
229
    Py_DECREF(py_groups);
2!
230
    PyDict_SetItemString(result, "source_files", py_sources);
2!
231
    Py_DECREF(py_sources);
2!
232
    PyDict_SetItemString(result, "tasks", py_tasks);
2!
233
    Py_DECREF(py_tasks);
2!
234
    PyDict_SetItemString(result, "total_events",
2!
235
                         PyLong_FromSize_t(plan.total_events));
2!
236
    return result;
2✔
237
}
2✔
238

239
static PyObject *ReorganizationPlanner_call(PyObject *self, PyObject *args,
1✔
240
                                            PyObject *kwds) {
241
    return ReorganizationPlanner_plan((ReorganizationPlannerObject *)self, args,
2✔
242
                                      kwds);
1✔
243
}
244

245
static PyMethodDef ReorganizationPlanner_methods[] = {
246
    {"process", (PyCFunction)ReorganizationPlanner_plan,
247
     METH_VARARGS | METH_KEYWORDS,
248
     "process(source_files, groups=None, index_dir='')\n"
249
     "--\n"
250
     "\n"
251
     "Build a reorganization plan for trace files.\n"
252
     "\n"
253
     "Args:\n"
254
     "    source_files (list[str]): Paths to source trace files.\n"
255
     "    groups (list[dict] or None): Predicate group definitions\n"
256
     "        (default None).\n"
257
     "    index_dir (str): Directory for index sidecars (default '').\n"
258
     "\n"
259
     "Returns:\n"
260
     "    dict: Extraction plan.\n"},
261
    {NULL} /* Sentinel */
262
};
263

264
PyTypeObject ReorganizationPlannerType = {
265
    PyVarObject_HEAD_INIT(
266
        NULL,
267
        0) "dftracer_utils_ext.ReorganizationPlannerUtility", /* tp_name */
268
    sizeof(ReorganizationPlannerObject),                      /* tp_basicsize */
269
    0,                                                        /* tp_itemsize */
270
    (destructor)ReorganizationPlanner_dealloc,                /* tp_dealloc */
271
    0,                                        /* tp_vectorcall_offset */
272
    0,                                        /* tp_getattr */
273
    0,                                        /* tp_setattr */
274
    0,                                        /* tp_as_async */
275
    0,                                        /* tp_repr */
276
    0,                                        /* tp_as_number */
277
    0,                                        /* tp_as_sequence */
278
    0,                                        /* tp_as_mapping */
279
    0,                                        /* tp_hash */
280
    ReorganizationPlanner_call,               /* tp_call */
281
    0,                                        /* tp_str */
282
    0,                                        /* tp_getattro */
283
    0,                                        /* tp_setattro */
284
    0,                                        /* tp_as_buffer */
285
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
286
    "ReorganizationPlannerUtility(runtime: Runtime | None = None)\n"
287
    "--\n"
288
    "\n"
289
    "Plan semantic reorganization of trace files into predicate groups.\n"
290
    "\n"
291
    "Args:\n"
292
    "    runtime (Runtime or None): Runtime for thread pool control.\n"
293
    "        If None, uses the default global Runtime.\n", /* tp_doc */
294
    0,                                                     /* tp_traverse */
295
    0,                                                     /* tp_clear */
296
    0,                                                     /* tp_richcompare */
297
    0,                                    /* tp_weaklistoffset */
298
    0,                                    /* tp_iter */
299
    0,                                    /* tp_iternext */
300
    ReorganizationPlanner_methods,        /* tp_methods */
301
    0,                                    /* tp_members */
302
    0,                                    /* tp_getset */
303
    0,                                    /* tp_base */
304
    0,                                    /* tp_dict */
305
    0,                                    /* tp_descr_get */
306
    0,                                    /* tp_descr_set */
307
    0,                                    /* tp_dictoffset */
308
    (initproc)ReorganizationPlanner_init, /* tp_init */
309
    0,                                    /* tp_alloc */
310
    ReorganizationPlanner_new,            /* tp_new */
311
};
312

313
int init_reorganization_planner(PyObject *m) {
1✔
314
    if (PyType_Ready(&ReorganizationPlannerType) < 0) return -1;
1!
315

316
    Py_INCREF(&ReorganizationPlannerType);
1✔
317
    if (PyModule_AddObject(m, "ReorganizationPlannerUtility",
2!
318
                           (PyObject *)&ReorganizationPlannerType) < 0) {
1✔
UNCOV
319
        Py_DECREF(&ReorganizationPlannerType);
×
UNCOV
320
        Py_DECREF(m);
×
321
        return -1;
×
322
    }
323

324
    return 0;
1✔
325
}
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