Skip to content

Commit

Permalink
feat: add support for archives
Browse files Browse the repository at this point in the history
This patch allows hotspot to open zip, tar, ... files
  • Loading branch information
lievenhey committed Nov 19, 2024
1 parent 3e74a87 commit f375adf
Showing 1 changed file with 108 additions and 7 deletions.
115 changes: 108 additions & 7 deletions src/parsers/perf/perfparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@
#include "settings.h"

#if KFArchive_FOUND
#include <K7Zip>
#include <KCompressionDevice>
#include <KTar>
#include <KZip>

#include <QMimeDatabase>
#endif

Q_LOGGING_CATEGORY(LOG_PERFPARSER, "hotspot.perfparser", QtWarningMsg)
Expand Down Expand Up @@ -2043,19 +2048,112 @@ void PerfParser::exportResults(const QUrl& url)
});
}

// helper for extracting files from archives
namespace {
#if KFArchive_FOUND
// create archive reader from mimetype
auto getArchiveFromMime = [](const QString& filename, const QMimeType& mimeType) -> std::unique_ptr<KArchive> {
if (mimeType.name() == QLatin1String("application/x-tar")) {
return std::make_unique<KTar>(filename);
} else if (mimeType.name() == QLatin1String("application/zip")) {
return std::make_unique<KZip>(filename);
} else if (mimeType.name() == QLatin1String("application/x-7z-compressed")) {
return std::make_unique<K7Zip>(filename);
}
return {};
};

auto extractFromArchive = [](const std::unique_ptr<KArchive>& archive) -> std::unique_ptr<QTemporaryFile> {
auto extractFile = [](const KArchiveDirectory* directory,
const QString& filename) -> std::unique_ptr<QTemporaryFile> {
auto extracted = std::make_unique<QTemporaryFile>();
extracted->open();

auto fileToExtract = directory->file(filename);
if (!fileToExtract) {
return {};
}

auto fileToExtractHandle = fileToExtract->createDevice();

const int chunkSize = 1024 * 100;

QByteArray buffer;
buffer.resize(chunkSize);

while (!fileToExtractHandle->atEnd()) {
const auto size = fileToExtractHandle->read(buffer.data(), buffer.size());
extracted->write(buffer.data(), size);
}
extracted->flush();

return extracted;
};

if (!archive->open(QIODevice::ReadOnly)) {
qWarning() << "Failed to open archive:" << archive->errorString();
return {};
}

auto dir = archive->directory();
auto entries = dir->entries();

if (entries.size() == 1) {
return extractFile(dir, entries[0]);
}

for (const auto& file : {QStringLiteral("perf.data"), QStringLiteral("perf.data.perfparser")}) {
if (entries.contains(file)) {
return extractFile(dir, file);
}
}

return {};
};
#endif // KFArchive_FOUND
}

QString PerfParser::decompressIfNeeded(const QString& path)
{
#if KFArchive_FOUND
m_decompressed = std::make_unique<QTemporaryFile>(this);

KCompressionDevice compressedFile(path);

QMimeDatabase mimedb;

// extract perf.data file form archive, on success set m_decompressed to that file
// otherwise return the archive path
auto extractArchive = [this, &mimedb](const QString& path) {
const auto mimetype = mimedb.mimeTypeForFile(path);
auto archive = getArchiveFromMime(path, mimetype);

if (!archive) {
// we don't have and archive -> return original file
return path;
}

auto extracted = extractFromArchive(archive);
if (extracted) {
m_decompressed = std::move(extracted);
}
return m_decompressed->fileName();
};

// uncompressed file -> check if it is an archive (tar for example)
// extractArchive returns the original path if it couldn't open the archive
if (compressedFile.compressionType() == KCompressionDevice::None) {
return extractArchive(path);
}

if (!compressedFile.open(QIODevice::ReadOnly)) {
// we failed to open the compressed file
qWarning() << "Failed to open:" << path;
return path;
}

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

const int chunkSize = 1024 * 100;

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

while (!compressedFile.atEnd()) {
const auto size = compressedFile.read(buffer.data(), buffer.size());
m_decompressed->write(buffer.data(), size);
decompressed->write(buffer.data(), size);
}
m_decompressed->flush();
decompressed->flush();

compressedFile.close();
return m_decompressed->fileName();
m_decompressed = std::move(decompressed);
}

// if m_decompressed is not an archive, this will return m_decompressed
return extractArchive(m_decompressed->fileName());
#endif
// fallback
return path;
Expand Down

0 comments on commit f375adf

Please sign in to comment.