Skip to content

Commit f375adf

Browse files
committed
feat: add support for archives
This patch allows hotspot to open zip, tar, ... files
1 parent 3e74a87 commit f375adf

File tree

1 file changed

+108
-7
lines changed

1 file changed

+108
-7
lines changed

src/parsers/perf/perfparser.cpp

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@
3434
#include "settings.h"
3535

3636
#if KFArchive_FOUND
37+
#include <K7Zip>
3738
#include <KCompressionDevice>
39+
#include <KTar>
40+
#include <KZip>
41+
42+
#include <QMimeDatabase>
3843
#endif
3944

4045
Q_LOGGING_CATEGORY(LOG_PERFPARSER, "hotspot.perfparser", QtWarningMsg)
@@ -2043,19 +2048,112 @@ void PerfParser::exportResults(const QUrl& url)
20432048
});
20442049
}
20452050

2051+
// helper for extracting files from archives
2052+
namespace {
2053+
#if KFArchive_FOUND
2054+
// create archive reader from mimetype
2055+
auto getArchiveFromMime = [](const QString& filename, const QMimeType& mimeType) -> std::unique_ptr<KArchive> {
2056+
if (mimeType.name() == QLatin1String("application/x-tar")) {
2057+
return std::make_unique<KTar>(filename);
2058+
} else if (mimeType.name() == QLatin1String("application/zip")) {
2059+
return std::make_unique<KZip>(filename);
2060+
} else if (mimeType.name() == QLatin1String("application/x-7z-compressed")) {
2061+
return std::make_unique<K7Zip>(filename);
2062+
}
2063+
return {};
2064+
};
2065+
2066+
auto extractFromArchive = [](const std::unique_ptr<KArchive>& archive) -> std::unique_ptr<QTemporaryFile> {
2067+
auto extractFile = [](const KArchiveDirectory* directory,
2068+
const QString& filename) -> std::unique_ptr<QTemporaryFile> {
2069+
auto extracted = std::make_unique<QTemporaryFile>();
2070+
extracted->open();
2071+
2072+
auto fileToExtract = directory->file(filename);
2073+
if (!fileToExtract) {
2074+
return {};
2075+
}
2076+
2077+
auto fileToExtractHandle = fileToExtract->createDevice();
2078+
2079+
const int chunkSize = 1024 * 100;
2080+
2081+
QByteArray buffer;
2082+
buffer.resize(chunkSize);
2083+
2084+
while (!fileToExtractHandle->atEnd()) {
2085+
const auto size = fileToExtractHandle->read(buffer.data(), buffer.size());
2086+
extracted->write(buffer.data(), size);
2087+
}
2088+
extracted->flush();
2089+
2090+
return extracted;
2091+
};
2092+
2093+
if (!archive->open(QIODevice::ReadOnly)) {
2094+
qWarning() << "Failed to open archive:" << archive->errorString();
2095+
return {};
2096+
}
2097+
2098+
auto dir = archive->directory();
2099+
auto entries = dir->entries();
2100+
2101+
if (entries.size() == 1) {
2102+
return extractFile(dir, entries[0]);
2103+
}
2104+
2105+
for (const auto& file : {QStringLiteral("perf.data"), QStringLiteral("perf.data.perfparser")}) {
2106+
if (entries.contains(file)) {
2107+
return extractFile(dir, file);
2108+
}
2109+
}
2110+
2111+
return {};
2112+
};
2113+
#endif // KFArchive_FOUND
2114+
}
2115+
20462116
QString PerfParser::decompressIfNeeded(const QString& path)
20472117
{
20482118
#if KFArchive_FOUND
2049-
m_decompressed = std::make_unique<QTemporaryFile>(this);
2050-
20512119
KCompressionDevice compressedFile(path);
20522120

2121+
QMimeDatabase mimedb;
2122+
2123+
// extract perf.data file form archive, on success set m_decompressed to that file
2124+
// otherwise return the archive path
2125+
auto extractArchive = [this, &mimedb](const QString& path) {
2126+
const auto mimetype = mimedb.mimeTypeForFile(path);
2127+
auto archive = getArchiveFromMime(path, mimetype);
2128+
2129+
if (!archive) {
2130+
// we don't have and archive -> return original file
2131+
return path;
2132+
}
2133+
2134+
auto extracted = extractFromArchive(archive);
2135+
if (extracted) {
2136+
m_decompressed = std::move(extracted);
2137+
}
2138+
return m_decompressed->fileName();
2139+
};
2140+
2141+
// uncompressed file -> check if it is an archive (tar for example)
2142+
// extractArchive returns the original path if it couldn't open the archive
20532143
if (compressedFile.compressionType() == KCompressionDevice::None) {
2144+
return extractArchive(path);
2145+
}
2146+
2147+
if (!compressedFile.open(QIODevice::ReadOnly)) {
2148+
// we failed to open the compressed file
2149+
qWarning() << "Failed to open:" << path;
20542150
return path;
20552151
}
20562152

2057-
if (compressedFile.open(QIODevice::ReadOnly)) {
2058-
m_decompressed->open();
2153+
// we now have a compressed file that could be an archive -> decompress
2154+
{
2155+
auto decompressed = std::make_unique<QTemporaryFile>();
2156+
decompressed->open();
20592157

20602158
const int chunkSize = 1024 * 100;
20612159

@@ -2064,13 +2162,16 @@ QString PerfParser::decompressIfNeeded(const QString& path)
20642162

20652163
while (!compressedFile.atEnd()) {
20662164
const auto size = compressedFile.read(buffer.data(), buffer.size());
2067-
m_decompressed->write(buffer.data(), size);
2165+
decompressed->write(buffer.data(), size);
20682166
}
2069-
m_decompressed->flush();
2167+
decompressed->flush();
20702168

20712169
compressedFile.close();
2072-
return m_decompressed->fileName();
2170+
m_decompressed = std::move(decompressed);
20732171
}
2172+
2173+
// if m_decompressed is not an archive, this will return m_decompressed
2174+
return extractArchive(m_decompressed->fileName());
20742175
#endif
20752176
// fallback
20762177
return path;

0 commit comments

Comments
 (0)