1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "taskmodel.h"
6
7#include <QIcon>
8#include <QFontMetrics>
9
10extern void *kTimeline;
11
12namespace ReverseDebugger {
13namespace Internal {
14
15namespace Core {
16static QString Id()
17{
18 return "";
19}
20}
21
22uint Task::s_nextId = 1;
23
24ReverseDebugger::Internal::Task::Task(
25 const QString &_description, const QString &_category, const EventEntry *_event)
26 : taskId(s_nextId), event(_event), category(_category), description(_description)
27{
28 ++s_nextId;
29}
30
31bool Task::isNull() const
32{
33 return taskId == 0;
34}
35
36void Task::clear()
37{
38 taskId = 0;
39 description.clear();
40 category.clear();
41}
42
43bool operator==(const Task &t1, const Task &t2)
44{
45 return t1.taskId == t2.taskId;
46}
47
48bool operator<(const Task &a, const Task &b)
49{
50 return a.taskId < b.taskId;
51}
52
53uint qHash(const Task &task)
54{
55 return task.taskId;
56}
57
58TaskModel::TaskModel(QObject *parent)
59{
60 Q_UNUSED(parent)
61}
62
63void TaskModel::setTimelinePtr(void *timeline)
64{
65 this->timeline = timeline;
66}
67
68QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
69{
70 if (parent.isValid())
71 return QModelIndex();
72 return createIndex(row, column);
73}
74
75QModelIndex TaskModel::parent(const QModelIndex &child) const
76{
77 Q_UNUSED(child)
78 return QModelIndex();
79}
80
81int TaskModel::rowCount(const QModelIndex &parent) const
82{
83 return parent.isValid() ? 0 : allTasks.count();
84}
85
86int TaskModel::columnCount(const QModelIndex &parent) const
87{
88 return parent.isValid() ? 0 : 1;
89}
90
91QVariant TaskModel::data(const QModelIndex &index, int role) const
92{
93 int row = index.row();
94 if (!index.isValid() || row < 0 || row >= allTasks.count() || index.column() != 0)
95 return QVariant();
96
97 if (role == TaskModel::Description)
98 return allTasks.at(row).description;
99 else if (role == TaskModel::Return)
100 return (long long)(allTasks.at(row).event->syscall_result);
101 else if (role == TaskModel::ThreadNum)
102 return allTasks.at(row).event->thread_num;
103 else if (role == TaskModel::Tid)
104 return allTasks.at(row).event->tid;
105 else if (role == TaskModel::Time)
106 return allTasks.at(row).event->time;
107 else if (role == TaskModel::Duration)
108 return allTasks.at(row).event->duration;
109 else if (role == TaskModel::Category)
110 // return m_tasks.at(row).category.uniqueIdentifier();
111 return allTasks.at(row).category;
112 else if (role == TaskModel::Icon)
113 return QIcon();
114 else if (role == TaskModel::ExtraInfo) {
115 QString info;
116 bool ok;
117 int i = 0;
118 auto task = allTasks.at(row);
119 for (; i < task.description.size(); ++i) {
120 if (task.description[i] > QLatin1Char('9')) {
121 break;
122 }
123 }
124 if (i > 0) {
125 i = task.description.left(i).toInt(&ok, 10);
126 if (ok) {
127 char buf[EVENT_EXTRA_INFO_SIZE];
128 if (get_event_extra_info(kTimeline,
129 i, buf, sizeof(buf))
130 > 0) {
131 info = QLatin1String(buf);
132 }
133
134 if (task.event->syscall_result < 0) {
135 info += QLatin1Char('(');
136 info += QLatin1String(errno_name(static_cast<int>(task.event->syscall_result)));
137 info += QLatin1Char(')');
138 }
139 }
140 }
141 return info;
142 }
143 return QVariant();
144}
145
146Task TaskModel::task(int row) const
147{
148 if (row < 0 || row >= allTasks.count())
149 return Task();
150 return allTasks.at(row);
151}
152
153Task TaskModel::task(const QModelIndex &index) const
154{
155 int row = index.row();
156 if (!index.isValid() || row < 0 || row >= allTasks.count())
157 return Task();
158 return allTasks.at(row);
159}
160
161QList<QString> TaskModel::categoryIds() const
162{
163 QList<QString> categoryList = categories.keys();
164 categoryList.removeAll(""); // remove global category we added for bookkeeping
165 return categoryList;
166}
167
168QString TaskModel::categoryDisplayName(const QString &categoryId) const
169{
170 return categories.value(categoryId).displayName;
171}
172
173void TaskModel::addCategory(const QString &categoryId, const QString &categoryName)
174{
175 if (categoryId.isEmpty())
176 return;
177
178 CategoryData data;
179 data.displayName = categoryName;
180 categories.insert(categoryId, data);
181}
182
183QList<Task> TaskModel::tasks(const QString &categoryId) const
184{
185 if (categoryId.isEmpty())
186 return allTasks;
187
188 QList<Task> taskList;
189 foreach (const Task &t, allTasks) {
190 if (t.category == categoryId)
191 taskList.append(t);
192 }
193 return taskList;
194}
195
196bool sortById(const Task &task, unsigned int id)
197{
198 return task.taskId < id;
199}
200
201void TaskModel::addTask(const Task &task)
202{
203 Q_ASSERT(categories.keys().contains(task.category));
204 CategoryData &data = categories[task.category];
205 CategoryData &global = categories[""];
206
207 QList<Task>::iterator it = std::lower_bound(allTasks.begin(), allTasks.end(), task.taskId, sortById);
208 int i = it - allTasks.begin();
209 beginInsertRows(QModelIndex(), i, i);
210 allTasks.insert(it, task);
211 data.addTask(task);
212 global.addTask(task);
213 endInsertRows();
214}
215
216void TaskModel::removeTask(const Task &task)
217{
218 int index = allTasks.indexOf(task);
219 if (index >= 0) {
220 const Task &t = allTasks.at(index);
221
222 beginRemoveRows(QModelIndex(), index, index);
223 categories[task.category].removeTask(t);
224 categories[Core::Id()].removeTask(t);
225 allTasks.removeAt(index);
226 endRemoveRows();
227 }
228}
229
230void TaskModel::clearTasks(const QString &categoryId)
231{
232 typedef QHash<QString, CategoryData>::ConstIterator IdCategoryConstIt;
233
234 if (categoryId.isEmpty()) {
235 if (allTasks.count() == 0)
236 return;
237 beginRemoveRows(QModelIndex(), 0, allTasks.count() - 1);
238 allTasks.clear();
239 const IdCategoryConstIt cend = categories.constEnd();
240 for (IdCategoryConstIt it = categories.constBegin(); it != cend; ++it)
241 categories[it.key()].clear();
242 endRemoveRows();
243 } else {
244 int index = 0;
245 int start = 0;
246 CategoryData &global = categories[Core::Id()];
247 CategoryData &cat = categories[categoryId];
248
249 while (index < allTasks.count()) {
250 while (index < allTasks.count() && allTasks.at(index).category != categoryId) {
251 ++start;
252 ++index;
253 }
254 if (index == allTasks.count())
255 break;
256 while (index < allTasks.count() && allTasks.at(index).category == categoryId)
257 ++index;
258
259 // Index is now on the first non category
260 beginRemoveRows(QModelIndex(), start, index - 1);
261
262 for (int i = start; i < index; ++i) {
263 global.removeTask(allTasks.at(i));
264 cat.removeTask(allTasks.at(i));
265 }
266
267 allTasks.erase(allTasks.begin() + start, allTasks.begin() + index);
268
269 endRemoveRows();
270 index = start;
271 }
272 }
273}
274
275int TaskModel::getSizeOfLineNumber(const QFont &font)
276{
277 if (sizeOfLineNumber == 0 || font != lineMeasurementFont) {
278 QFontMetrics fm(font);
279 lineMeasurementFont = font;
280 sizeOfLineNumber = fm.horizontalAdvance(QLatin1String("88888"));
281 }
282 return sizeOfLineNumber;
283}
284
285int TaskModel::taskCount(const QString &categoryId)
286{
287 return categories.value(categoryId).count;
288}
289
290int TaskModel::rowForId(unsigned int id)
291{
292 QList<Task>::const_iterator it = std::lower_bound(allTasks.constBegin(), allTasks.constEnd(), id, sortById);
293 if (it == allTasks.constEnd())
294 return -1;
295 return it - allTasks.constBegin();
296}
297
298} // namespace Internal
299} // namespace ReverseDebugger
300