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

@@ -31,3 +31,4 @@ Detailed usage instructions and option references can be found in the [USAGE.md]
## Credits
- [CLI11: Command line parser for C++11](https://github.com/CLIUtils/CLI11)
- [PicoSHA2: A header-file-only, SHA256 hash generator in C++](https://github.com/okdshin/PicoSHA2)
- [nlohmann/json: JSON for Modern C++](https://github.com/nlohmann/json)

21
include/LICENSE.nlohmann Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2025 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

25526
include/nlohmann/json.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
#include <cstdint> // int64_t, uint64_t
#include <map> // map
#include <memory> // allocator
#include <string> // string
#include <vector> // vector
// #include <nlohmann/detail/abi_macros.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// This file contains all macro definitions affecting or depending on the ABI
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
#warning "Already included a different version of the library!"
#endif
#endif
#endif
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
#endif
#ifndef JSON_DIAGNOSTIC_POSITIONS
#define JSON_DIAGNOSTIC_POSITIONS 0
#endif
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
#endif
#if JSON_DIAGNOSTICS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
#endif
#if JSON_DIAGNOSTIC_POSITIONS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
#else
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
#endif
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
#else
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
#endif
// Construct the namespace ABI tags component
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
_v ## major ## _ ## minor ## _ ## patch
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_VERSION
#else
#define NLOHMANN_JSON_NAMESPACE_VERSION \
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
NLOHMANN_JSON_VERSION_MINOR, \
NLOHMANN_JSON_VERSION_PATCH)
#endif
// Combine namespace components
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
#ifndef NLOHMANN_JSON_NAMESPACE
#define NLOHMANN_JSON_NAMESPACE \
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
NLOHMANN_JSON_ABI_TAGS, \
NLOHMANN_JSON_NAMESPACE_VERSION)
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
namespace nlohmann \
{ \
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
NLOHMANN_JSON_ABI_TAGS, \
NLOHMANN_JSON_NAMESPACE_VERSION) \
{
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_END
#define NLOHMANN_JSON_NAMESPACE_END \
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
} // namespace nlohmann
#endif
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
NLOHMANN_JSON_NAMESPACE_BEGIN
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
/// a class to store JSON values
/// @sa https://json.nlohmann.me/api/basic_json/
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::string, class BooleanType = bool,
class NumberIntegerType = std::int64_t,
class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer,
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
class CustomBaseClass = void>
class basic_json;
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
/// @sa https://json.nlohmann.me/api/json_pointer/
template<typename RefStringType>
class json_pointer;
/*!
@brief default specialization
@sa https://json.nlohmann.me/api/json/
*/
using json = basic_json<>;
/// @brief a minimal map-like container that preserves insertion order
/// @sa https://json.nlohmann.me/api/ordered_map/
template<class Key, class T, class IgnoredLess, class Allocator>
struct ordered_map;
/// @brief specialization that maintains the insertion order of object keys
/// @sa https://json.nlohmann.me/api/ordered_json/
using ordered_json = basic_json<nlohmann::ordered_map>;
NLOHMANN_JSON_NAMESPACE_END
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_

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;

View File

@@ -81,14 +81,14 @@ private:
std::vector<uint8_t *> _ptrs_u;
std::vector<FILE *> _fps;
std::vector<int> _fds;
std::vector<std::string_view> _files;
std::vector<std::string> _files;
public:
~garbageCollector();
void delAfterProgress(char *&_ptr);
void delAfterProgress(uint8_t *&_ptr);
void delFileAfterProgress(std::string_view path);
void delFileAfterProgress(const std::string& path);
void closeAfterProgress(FILE *&_fp);
void closeAfterProgress(int _fd);
};

View File

@@ -95,7 +95,7 @@ garbageCollector::~garbageCollector() {
close(fd);
for (const auto &fp : _fps)
fclose(fp);
for (const auto &file: _files)
for (const auto &file : _files)
eraseEntry(file);
}
@@ -105,7 +105,7 @@ void garbageCollector::delAfterProgress(char *&_ptr) {
void garbageCollector::delAfterProgress(uint8_t *&_ptr) {
_ptrs_u.push_back(_ptr);
}
void garbageCollector::delFileAfterProgress(const std::string_view path) {
void garbageCollector::delFileAfterProgress(const std::string &path) {
_files.push_back(path);
}
void garbageCollector::closeAfterProgress(const int _fd) {

View File

@@ -118,6 +118,7 @@ public:
[[nodiscard]] constant_iterator cend() const;
};
using Partition_t = _entry;
using Map_t = basic_partition_map;
class basic_partition_map_builder final {

View File

@@ -47,7 +47,7 @@ bool basic_partition_map_builder::_is_real_block_dir(
}
Map_t basic_partition_map_builder::_build_map(std::string_view path,
bool logical) {
const bool logical) {
Map_t map;
std::vector<std::filesystem::directory_entry> entries{
std::filesystem::directory_iterator(path),