1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "mainwindow.h" |
6 | #include "client.h" |
7 | #include "jsontabwidget.h" |
8 | #include "perfflamegraphscripts.h" |
9 | #include "perfrecorddisplay.h" |
10 | #include "jsontabwidget.h" |
11 | #include "common/common.h" |
12 | |
13 | #include <QTime> |
14 | #include <QMenu> |
15 | #include <QMenuBar> |
16 | #include <QToolBar> |
17 | #include <QDockWidget> |
18 | #include <QToolButton> |
19 | #include <QLineEdit> |
20 | #include <QRegExpValidator> |
21 | |
22 | class MainWindowPrivate |
23 | { |
24 | friend class MainWindow; |
25 | JsonTabWidget *jsonTabWidget{nullptr}; |
26 | QToolBar *toolbar{nullptr}; |
27 | QLineEdit *editPid{nullptr}; |
28 | QToolButton *ctrlButton{nullptr}; |
29 | QDockWidget *perfRecordDock{nullptr}; |
30 | PerfRecordDisplay *perfRecordDisplay{nullptr}; |
31 | FlameGraphGenTask *flameGraphGenTask{nullptr}; |
32 | |
33 | Client *client{nullptr}; |
34 | QThread *cliThread{nullptr}; |
35 | QProcess *server{nullptr}; |
36 | QTimer *timer{nullptr}; |
37 | QString attach{QMenu::tr("Attach" )}; |
38 | QString strat{QToolBar::tr("Start" )}; |
39 | QString stop{QToolBar::tr("Stop" )}; |
40 | QString fromPid{QAction::tr("Pid" )}; |
41 | QString fromProgram{QAction::tr("Program" )}; |
42 | std::string host{"127.0.0.1" }; |
43 | int port{3309}; |
44 | }; |
45 | |
46 | MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) |
47 | : QMainWindow(parent, flags) |
48 | , d (new MainWindowPrivate) |
49 | { |
50 | d->toolbar = new QToolBar; |
51 | d->editPid = new QLineEdit(); |
52 | d->editPid->setPlaceholderText("PID" ); |
53 | QRegularExpression regx("[0-9]+$" ); |
54 | QValidator *validator = new QRegularExpressionValidator(regx, d->editPid); |
55 | d->editPid->setValidator(validator); |
56 | d->ctrlButton = new QToolButton; |
57 | d->perfRecordDock = new QDockWidget; |
58 | d->perfRecordDisplay = new PerfRecordDisplay; |
59 | d->jsonTabWidget = new JsonTabWidget; |
60 | |
61 | d->ctrlButton->setChecked(true); |
62 | d->ctrlButton->setCheckable(true); |
63 | d->ctrlButton->setText(d->strat); |
64 | QObject::connect(d->ctrlButton, &QToolButton::toggled, [=](bool checked){ |
65 | if (checked) { |
66 | QString pid = d->editPid->text(); |
67 | if (pid.isEmpty()) { |
68 | ContextDialog::okCancel(QMessageBox::tr("attach processId can't empty!" )); |
69 | return; |
70 | } |
71 | |
72 | QByteArray queryPidLines; |
73 | ProcessUtil::execute("ps" , {"--pid" , pid}, |
74 | [&queryPidLines](const QByteArray &data) { |
75 | queryPidLines = data; |
76 | }); |
77 | if (queryPidLines.isEmpty() || queryPidLines.count('\n') < 2) { |
78 | ContextDialog::okCancel(QMessageBox::tr("attach processId no exites!" )); |
79 | return; |
80 | } |
81 | |
82 | d->ctrlButton->setText(d->stop); |
83 | start(d->editPid->text().toUInt()); |
84 | } else { |
85 | d->ctrlButton->setText(d->strat); |
86 | stop(); |
87 | } |
88 | }); |
89 | |
90 | d->toolbar->addSeparator(); |
91 | d->toolbar->addWidget(d->editPid); |
92 | d->toolbar->addWidget(d->ctrlButton); |
93 | addToolBar(d->toolbar); |
94 | |
95 | d->perfRecordDock->setWidget(d->perfRecordDisplay); |
96 | // TODO(Any): disabled until Use hotpot(https://github.com/KDAB/hotspot) to refactor |
97 | // addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, d->perfRecordDock); |
98 | |
99 | setCentralWidget(d->jsonTabWidget); |
100 | } |
101 | |
102 | MainWindow::~MainWindow() |
103 | { |
104 | stop(); |
105 | |
106 | if (d) { |
107 | delete d; |
108 | } |
109 | } |
110 | |
111 | void MainWindow::start(uint pid) |
112 | { |
113 | if (!d->server) { |
114 | QString toolsPath = CustomPaths::global(CustomPaths::Tools); |
115 | QString adapter = toolsPath + QDir::separator() + "performanceadapter" ; |
116 | d->server = new QProcess(); |
117 | |
118 | QObject::connect(d->server, &QProcess::errorOccurred, [=](QProcess::ProcessError error){ |
119 | qCritical() << "server >> " << error << d->server->errorString(); |
120 | }); |
121 | QObject::connect(d->server, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), |
122 | [](int exit, QProcess::ExitStatus status) { |
123 | qCritical() << "server >> " |
124 | << "exit: " << exit |
125 | << "status: " << status; |
126 | }); |
127 | #ifdef QT_DEBUG |
128 | QObject::connect(d->server, &QProcess::readyReadStandardError, [=](){ |
129 | qInfo() << "server stderr >> " << d->server->readAllStandardError(); |
130 | }); |
131 | QObject::connect(d->server, &QProcess::readyReadStandardOutput, [=](){ |
132 | qInfo() << "server stdout >> " << d->server->readAllStandardOutput(); |
133 | }); |
134 | #endif |
135 | qInfo() << adapter << "--port" << d->port; |
136 | d->server->start(adapter, {"--port" , QString::number(d->port)}); |
137 | d->server->waitForReadyRead(); |
138 | } |
139 | |
140 | if (!d->client) { |
141 | d->client = new Client(d->host, d->port); |
142 | auto cliThread = new QThread(); |
143 | d->client->moveToThread(cliThread); |
144 | QObject::connect(d->client, &Client::pullDataResult, |
145 | d->jsonTabWidget, &JsonTabWidget::parseJson, |
146 | Qt::UniqueConnection); |
147 | cliThread->start(); |
148 | } |
149 | |
150 | QObject::metaObject()->invokeMethod(d->client, "initialzation" , Q_ARG(int, pid)); |
151 | |
152 | d->timer = new QTimer(this); |
153 | QObject::connect(d->timer, &QTimer::timeout, [=](){ |
154 | if (d->client) { |
155 | QObject::metaObject()->invokeMethod(d->client, "pullData" ); |
156 | } else { |
157 | d->timer->stop(); |
158 | } |
159 | }); |
160 | d->timer->start(1000); |
161 | |
162 | |
163 | if (!d->flameGraphGenTask) { |
164 | d->flameGraphGenTask = new FlameGraphGenTask; |
165 | QObject::connect(d->perfRecordDisplay, &PerfRecordDisplay::showWebBrowserGP, |
166 | d->flameGraphGenTask, &FlameGraphGenTask::showWebBrowser, |
167 | Qt::UniqueConnection); |
168 | |
169 | } |
170 | d->flameGraphGenTask->start(pid); |
171 | } |
172 | |
173 | void MainWindow::stop() |
174 | { |
175 | if (d->flameGraphGenTask) { |
176 | d->flameGraphGenTask->stop(); |
177 | } |
178 | |
179 | if (d->timer) { |
180 | d->timer->stop(); |
181 | delete d->timer; |
182 | d->timer = nullptr; |
183 | } |
184 | |
185 | if (d->cliThread) { |
186 | d->cliThread->exit(0); |
187 | delete d->cliThread; |
188 | d->cliThread = nullptr; |
189 | } |
190 | |
191 | if (d->client) { |
192 | d->client->shutdown(); |
193 | d->client->exit(); |
194 | delete d->client; |
195 | d->client = nullptr; |
196 | } |
197 | |
198 | if (d->server) { |
199 | d->server->kill(); |
200 | delete d->server; |
201 | d->server = nullptr; |
202 | } |
203 | } |
204 | |