pmt: fix memory leaks, and improve info function

- Removed --no-write-test argument of memory test function
 - Fixed memory leak of memory test function
 - Improved info function with using nlohmann/json header-only library
 - Some other improvents
This commit is contained in:
2025-08-14 12:56:29 +03:00
parent bf0df8cc83
commit 563e8a583e
12 changed files with 25795 additions and 48 deletions

View File

@@ -119,9 +119,9 @@ int Main(int argc, char **argv) {
"(--search-path)");
if (!Helper::hasSuperUser()) {
if (!((FuncManager.isUsed("rebootFunction") ||
FuncManager.isUsed("memoryTestFunction")) &&
Helper::hasAdbPermissions()))
if (!((FuncManager.isUsed("rebootFunction") &&
Helper::hasAdbPermissions()) ||
FuncManager.isUsed("memoryTestFunction")))
throw Error(
"Partition Manager Tool is requires super-user privileges!\n");
}

View File

@@ -19,7 +19,7 @@ Copyright 2025 Yağız Zengin
#include <cerrno>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#include <nlohmann/json.hpp>
#define IFUN "infoFunction"
@@ -39,14 +39,17 @@ bool infoFunction::init(CLI::App &_app) {
"be written separately")
->default_val(false);
cmd->add_option("--json-partition-name", jNamePartition,
"Speficy partition name element for JSON body")
"Specify partition name element for JSON body")
->default_val("name");
cmd->add_option("--json-size-name", jNameSize,
"Speficy size element name for JSON body")
"Specify size element name for JSON body")
->default_val("size");
cmd->add_option("--json-logical-name", jNameLogical,
"Speficy logical element name for JSON body")
"Specify logical element name for JSON body")
->default_val("isLogical");
cmd->add_option("--json-indent-size", jIndentSize,
"Set JSON indent size for printing to screen")
->default_val(2);
return true;
}
@@ -63,6 +66,7 @@ bool infoFunction::run() {
partitions.push_back(name);
}
std::vector<PartitionMap::Partition_t> jParts;
for (const auto &partition : partitions) {
if (!Variables->PartMap->hasPartition(partition))
throw Error("Couldn't find partition: %s", partition.data());
@@ -79,14 +83,9 @@ bool infoFunction::run() {
}
if (jsonFormat)
#ifdef __LP64__
println("{\"%s\": \"%s\", \"%s\": %lu, \"%s\": %s}",
#else
println("{\"%s\": \"%s\", \"%s\": %llu, \"%s\": %s}",
#endif
jNamePartition.data(), partition.data(), jNameSize.data(),
Variables->PartMap->sizeOf(partition), jNameLogical.data(),
Variables->PartMap->isLogical(partition) ? "true" : "false");
jParts.push_back({partition,
{Variables->PartMap->sizeOf(partition),
Variables->PartMap->isLogical(partition)}});
else
#ifdef __LP64__
println("partition=%s size=%lu isLogical=%s",
@@ -97,6 +96,18 @@ bool infoFunction::run() {
Variables->PartMap->isLogical(partition) ? "true" : "false");
}
if (jsonFormat) {
nlohmann::json j;
j["partitions"] = nlohmann::json::array();
for (const auto &[name, props] : jParts) {
j["partitions"].push_back({{jNamePartition, name},
{jNameSize, props.size},
{jNameLogical, props.isLogical}});
}
println("%s", j.dump(jIndentSize).data());
}
return true;
}

View File

@@ -34,17 +34,21 @@ bool memoryTestFunction::init(CLI::App &_app) {
cmd->add_option("testDirectory", testPath, "Path to test directory")
->default_val("/data/local/tmp")
->check([&](const std::string &val) {
if (val.find("/sdcard") != std::string::npos ||
val.find("/storage") != std::string::npos)
return std::string(
"Sequential read tests on FUSE-mounted paths do not give correct "
"results, so its use is prohibited (by pmt)!");
if (val != "/data/local/tmp" && !Helper::directoryIsExists(val))
return std::string("Couldn't find directory: " + val +
", no root? Try executing in ADB shell.");
return std::string();
});
cmd->add_option("-s,--file-size", testFileSize, "File size of test file")
->transform(CLI::AsSizeValue(false))
->default_val("1GB");
cmd->add_flag("--no-write-test", doNotWriteTest,
"Don't write test data to disk")
->default_val(false);
cmd->add_flag("--no-read-test", doNotReadTest,
"Don't read test data from disk")
->default_val(false);
@@ -53,13 +57,9 @@ bool memoryTestFunction::init(CLI::App &_app) {
}
bool memoryTestFunction::run() {
if (doNotReadTest && doNotWriteTest)
throw Error("There must be at least one test transaction, but all of them "
"are blocked");
LOGN(MTFUN, INFO) << "Starting memory test on " << testPath << std::endl;
Helper::garbageCollector collector;
const std::string test = testPath + "/test.bin";
const std::string test = Helper::pathJoin(testPath, "test.bin");
LOGN(MTFUN, INFO) << "Generating random data for testing" << std::endl;
auto *buffer = new (std::nothrow) char[bufferSize];
@@ -72,25 +72,24 @@ bool memoryTestFunction::run() {
collector.delFileAfterProgress(test);
if (!doNotWriteTest) {
const int wfd = Helper::openAndAddToCloseList(
test, collector, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
if (wfd < 0)
throw Error("Can't open/create test file: %s", strerror(errno));
const int wfd = Helper::openAndAddToCloseList(
test, collector, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
if (wfd < 0)
throw Error("Can't open/create test file: %s\n", strerror(errno));
LOGN(MTFUN, INFO) << "Sequential write test started!" << std::endl;
const auto startWrite = std::chrono::high_resolution_clock::now();
ssize_t bytesWritten = 0;
while (bytesWritten < testFileSize) {
const ssize_t ret = write(wfd, buffer, bufferSize);
if (ret < 0) throw Error("Can't write to test file: %s\n", strerror(errno));
bytesWritten += ret;
LOGN(MTFUN, INFO) << "Sequential write test started!" << std::endl;
const auto startWrite = std::chrono::high_resolution_clock::now();
ssize_t bytesWritten = 0;
while (bytesWritten < testFileSize) {
const ssize_t ret = write(wfd, buffer, bufferSize);
if (ret < 0) throw Error("Can't write to test file: %s", strerror(errno));
bytesWritten += ret;
}
const auto endWrite = std::chrono::high_resolution_clock::now();
const double writeTime =
std::chrono::duration<double>(endWrite - startWrite).count();
println("Sequential write speed: %f MB/s",
println("Sequential write speed: %3.f MB/s",
(static_cast<double>(testFileSize) / (1024.0 * 1024.0)) /
writeTime);
LOGN(MTFUN, INFO) << "Sequential write test done!" << std::endl;
@@ -99,10 +98,11 @@ bool memoryTestFunction::run() {
if (!doNotReadTest) {
auto *rawBuffer = new char[bufferSize + 4096];
collector.delAfterProgress(rawBuffer);
auto *bufferRead = reinterpret_cast<char*>((reinterpret_cast<uintptr_t>(rawBuffer) + 4096 - 1) & ~(4096 - 1));
auto *bufferRead = reinterpret_cast<char *>(
(reinterpret_cast<uintptr_t>(rawBuffer) + 4096 - 1) & ~(4096 - 1));
const int rfd =
Helper::openAndAddToCloseList(test, collector, O_RDONLY | O_DIRECT);
if (rfd < 0) throw Error("Can't open test file: %s", strerror(errno));
if (rfd < 0) throw Error("Can't open test file: %s\n", strerror(errno));
LOGN(MTFUN, INFO) << "Sequential read test started!" << std::endl;
const auto startRead = std::chrono::high_resolution_clock::now();
@@ -115,9 +115,8 @@ bool memoryTestFunction::run() {
const double read_time =
std::chrono::duration<double>(endRead - startRead).count();
println("Sequential read speed: %f MB/s",
(static_cast<double>(total) / (1024.0 * 1024.0)) /
read_time);
println("Sequential read speed: %3.f MB/s",
(static_cast<double>(total) / (1024.0 * 1024.0)) / read_time);
LOGN(MTFUN, INFO) << "Sequential read test done!" << std::endl;
}

View File

@@ -101,6 +101,7 @@ class infoFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
std::string jNamePartition, jNameSize, jNameLogical;
int jIndentSize = 2;
bool jsonFormat = false;
public:
@@ -175,7 +176,7 @@ class memoryTestFunction final : public FunctionBase {
private:
uint64_t bufferSize = MB(4), /* bufferSizeRandom = KB(4),*/ testFileSize = 0;
std::string testPath;
bool doNotWriteTest = false, doNotReadTest = false;
bool doNotReadTest = false;
public:
CLI::App *cmd = nullptr;