1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "valgrindrunner.h" |
6 | #include "valgrindbar.h" |
7 | |
8 | #include "common/common.h" |
9 | #include "base/abstractaction.h" |
10 | #include "services/window/windowservice.h" |
11 | #include "services/builder/builderservice.h" |
12 | #include "services/language/languageservice.h" |
13 | |
14 | #include <QProcess> |
15 | #include <QDebug> |
16 | #include <QTextBlock> |
17 | |
18 | typedef FileOperation FO; |
19 | using namespace dpfservice; |
20 | |
21 | class ValgrindRunnerPrivate |
22 | { |
23 | friend class ValgrindRunner; |
24 | |
25 | QStringList ValgrindArgs; |
26 | dpfservice::ProjectInfo projectInfo; |
27 | QString activedProjectKitName; |
28 | QString workingDir; |
29 | QString currentFilePath; |
30 | QString targetPath; |
31 | QString xmlFilePath; |
32 | |
33 | QSharedPointer<QAction> memcheckAction; |
34 | QSharedPointer<QAction> helgrindAction; |
35 | }; |
36 | |
37 | ValgrindRunner::ValgrindRunner(QObject *parent) |
38 | : QObject(parent) |
39 | , d(new ValgrindRunnerPrivate) |
40 | { |
41 | |
42 | } |
43 | |
44 | void ValgrindRunner::initialize() |
45 | { |
46 | auto &ctx = dpfInstance.serviceContext(); |
47 | auto windowService = ctx.service<WindowService>(WindowService::name()); |
48 | if (!windowService) |
49 | return; |
50 | |
51 | d->memcheckAction.reset(new QAction(MWMAA_VALGRIND_MEMCHECK)); |
52 | ActionManager::getInstance()->registerAction(d->memcheckAction.get(), "Analyze.ValgrindMemcheck" , |
53 | d->memcheckAction->text(), QKeySequence()); |
54 | windowService->addAction(MWM_ANALYZE, new AbstractAction(d->memcheckAction.get())); |
55 | |
56 | d->helgrindAction.reset(new QAction(MWMAA_VALGRIND_HELGRIND)); |
57 | ActionManager::getInstance()->registerAction(d->helgrindAction.get(), "Analyze.ValgrindHelgrind" , |
58 | d->helgrindAction->text(), QKeySequence()); |
59 | windowService->addAction(MWM_ANALYZE, new AbstractAction(d->helgrindAction.get())); |
60 | |
61 | QObject::connect(d->memcheckAction.get(), &QAction::triggered, [=]() { |
62 | QtConcurrent::run([=]() { |
63 | ValgrindRunner::instance()->runValgrind("memcheck" ); |
64 | }); |
65 | }); |
66 | |
67 | QObject::connect(d->helgrindAction.get(), &QAction::triggered, [=]() { |
68 | QtConcurrent::run([=]() { |
69 | ValgrindRunner::instance()->runValgrind("helgrind" ); |
70 | }); |
71 | }); |
72 | |
73 | setActionsStatus(d->activedProjectKitName); |
74 | } |
75 | |
76 | void ValgrindRunner::runValgrind(const QString &type) |
77 | { |
78 | if(!checkValgrindToolPath()) |
79 | return; |
80 | runBuilding(); |
81 | setValgrindArgs(type); |
82 | QProcess procValgrind; |
83 | connect(&procValgrind, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), |
84 | [&]() { |
85 | emit valgrindFinished(d->xmlFilePath, type); |
86 | }); |
87 | |
88 | connect(&procValgrind, &QProcess::readyReadStandardError, [&]() { |
89 | procValgrind.setReadChannel(QProcess::StandardError); |
90 | while (procValgrind.canReadLine()) { |
91 | QString line = QString::fromUtf8(procValgrind.readLine()); |
92 | outputMsg(line, OutputPane::OutputFormat::StdErr); |
93 | } |
94 | }); |
95 | |
96 | connect(&procValgrind, &QProcess::readyReadStandardOutput, [&]() { |
97 | procValgrind.setReadChannel(QProcess::StandardError); |
98 | while (procValgrind.canReadLine()) { |
99 | QString line = QString::fromUtf8(procValgrind.readLine()); |
100 | outputMsg(line, OutputPane::OutputFormat::StdOut); |
101 | } |
102 | }); |
103 | |
104 | procValgrind.start("valgrind" , d->ValgrindArgs); |
105 | emit clearValgrindBar(type); |
106 | procValgrind.waitForFinished(-1); |
107 | } |
108 | |
109 | ValgrindRunner *ValgrindRunner::instance() |
110 | { |
111 | static ValgrindRunner ins; |
112 | return &ins; |
113 | } |
114 | |
115 | void ValgrindRunner::setValgrindArgs(const QString &type) |
116 | { |
117 | QString storage = FO::checkCreateDir(FO::checkCreateDir(d->workingDir, ".unioncode" ), "valgrind" ); |
118 | |
119 | if (MEMCHECK == type) { |
120 | d->ValgrindArgs.clear(); |
121 | d->xmlFilePath = storage + QDir::separator() + "memcheck.xml" ; |
122 | d->ValgrindArgs << "--leak-check=full" << "--xml=yes" << "--show-leak-kinds=definite" |
123 | << "--xml-file=" + d->xmlFilePath << d->targetPath; |
124 | } else if (HELGRIND == type) { |
125 | d->ValgrindArgs.clear(); |
126 | d->xmlFilePath = storage + QDir::separator() + "helgrind.xml" ; |
127 | d->ValgrindArgs << "--tool=helgrind" << "--xml=yes" << "--xml-file=" + d->xmlFilePath << d->targetPath; |
128 | } |
129 | } |
130 | |
131 | void ValgrindRunner::setMemcheckArgs(QStringList &args) |
132 | { |
133 | //TODO:add config arguments |
134 | args << "--leak-check=full" << "--xml=yes" << "--show-leak-kinds=definite" ; |
135 | } |
136 | |
137 | void ValgrindRunner::setHelgrindArgs(QStringList &args) |
138 | { |
139 | //TODO:add config arguments |
140 | args << "--tool=helgrind" << "--xml=yes" ; |
141 | } |
142 | |
143 | void ValgrindRunner::setActionsStatus(const QString &kitName) |
144 | { |
145 | if (kitName == "ninja" || kitName == "cmake" ) { |
146 | d->memcheckAction.get()->setEnabled(true); |
147 | d->helgrindAction.get()->setEnabled(true); |
148 | } else { |
149 | d->memcheckAction.get()->setEnabled(false); |
150 | d->helgrindAction.get()->setEnabled(false); |
151 | } |
152 | } |
153 | |
154 | void ValgrindRunner::saveCurrentProjectInfo(const ProjectInfo &projectInfo) |
155 | { |
156 | d->projectInfo = projectInfo; |
157 | d->activedProjectKitName = d->projectInfo.kitName(); |
158 | setActionsStatus(d->activedProjectKitName); |
159 | } |
160 | |
161 | void ValgrindRunner::removeProjectInfo() |
162 | { |
163 | d->activedProjectKitName.clear(); |
164 | setActionsStatus("" ); |
165 | } |
166 | |
167 | void ValgrindRunner::saveCurrentFilePath(const QString &filePath) |
168 | { |
169 | d->currentFilePath = filePath; |
170 | } |
171 | |
172 | void ValgrindRunner::removeCurrentFilePath() |
173 | { |
174 | d->currentFilePath.clear(); |
175 | } |
176 | |
177 | void ValgrindRunner::runBuilding() |
178 | { |
179 | auto &ctx = dpfInstance.serviceContext(); |
180 | LanguageService *service = ctx.service<LanguageService>(LanguageService::name()); |
181 | if (service) { |
182 | auto generator = service->create<LanguageGenerator>(d->activedProjectKitName); |
183 | if (generator) { |
184 | if (generator->isNeedBuild()) { |
185 | generator->build(d->projectInfo.workspaceFolder()); |
186 | } |
187 | RunCommandInfo args = generator->getRunArguments(d->projectInfo, d->currentFilePath); |
188 | d->targetPath = args.program.trimmed(); |
189 | d->workingDir = args.workingDir.trimmed(); |
190 | } |
191 | } |
192 | } |
193 | |
194 | void ValgrindRunner::outputMsg(const QString &content, OutputPane::OutputFormat format) |
195 | { |
196 | QMetaObject::invokeMethod(this, "printOutput" , Q_ARG(QString, content), Q_ARG(OutputPane::OutputFormat, format)); |
197 | } |
198 | |
199 | bool ValgrindRunner::checkValgrindToolPath() |
200 | { |
201 | if (!QFile("/usr/bin/valgrind" ).exists()) { |
202 | QString retMsg = tr("please install valgrind tool in console with \"sudo apt install valgrind\"." ); |
203 | outputMsg(retMsg, OutputPane::OutputFormat::StdErr); |
204 | return false; |
205 | } |
206 | return true; |
207 | } |
208 | |
209 | void ValgrindRunner::printOutput(const QString &content, OutputPane::OutputFormat format) |
210 | { |
211 | editor.switchContext(tr("&Application Output" )); |
212 | auto outputPane = OutputPane::instance(); |
213 | QString outputContent = content; |
214 | if (format == OutputPane::OutputFormat::NormalMessage) { |
215 | QTextDocument *doc = outputPane->document(); |
216 | QTextBlock tb = doc->lastBlock(); |
217 | QString lastLineText = tb.text(); |
218 | QString prefix = "\n" ; |
219 | if (lastLineText.isEmpty()) { |
220 | prefix = "" ; |
221 | } |
222 | QDateTime curDatetime = QDateTime::currentDateTime(); |
223 | QString time = curDatetime.toString("hh:mm:ss" ); |
224 | outputContent = prefix + time + ":" + content; |
225 | } |
226 | outputContent += "\n" ; |
227 | OutputPane::AppendMode mode = OutputPane::AppendMode::Normal; |
228 | outputPane->appendText(outputContent, format, mode); |
229 | } |
230 | |