summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsps-gold <79571312+sps-gold@users.noreply.github.com>2022-07-26 03:56:41 +0800
committerGitHub <noreply@github.com>2022-07-25 12:56:41 -0700
commit3a8d28f421a9452aff8f7ed2b140e12e2f314322 (patch)
tree8c67e20249568785d89796a24780c50640091463 /src
parent68e2ed11e86e72cc4e12a1b4026ef19d5149fda9 (diff)
downloadbinaryen-3a8d28f421a9452aff8f7ed2b140e12e2f314322.tar.gz
binaryen-3a8d28f421a9452aff8f7ed2b140e12e2f314322.tar.bz2
binaryen-3a8d28f421a9452aff8f7ed2b140e12e2f314322.zip
[wasm-split] Add --print-profile option (#4771)
There are several reasons why a function may not be trained in deterministically. So to perform quick validation we need to inspect profile.data (another ways requires split to be performed). However as profile.data is a binary file and is not self sufficient, so we cannot currently use it to perform such validation. Therefore to allow quick check on whether a particular function has been trained in, we need to dump profile.data in a more readable format. This PR, allows us to output, the list of functions to be kept (in main wasm) and those split functions (to be moved to deferred.wasm) in a readable format, to console. Added a new option `--print-profile` - input path to orig.wasm (its the original wasm file that will be used later during split) - input path to profile.data that we need to output optionally pass `--unescape` to unescape the function names Usage: ``` binaryen\build>bin\wasm-split.exe test\profile_data\MY.orig.wasm --print-profile=test\profile_data\profile.data > test\profile_data\out.log ``` note: meaning of prefixes `+` => fn to be kept in main wasm `-` => fn to be split and moved to deferred wasm
Diffstat (limited to 'src')
-rw-r--r--src/tools/wasm-split/split-options.cpp24
-rw-r--r--src/tools/wasm-split/split-options.h4
-rw-r--r--src/tools/wasm-split/wasm-split.cpp109
3 files changed, 118 insertions, 19 deletions
diff --git a/src/tools/wasm-split/split-options.cpp b/src/tools/wasm-split/split-options.cpp
index b5929aad7..b8ccde83e 100644
--- a/src/tools/wasm-split/split-options.cpp
+++ b/src/tools/wasm-split/split-options.cpp
@@ -67,6 +67,9 @@ std::ostream& operator<<(std::ostream& o, WasmSplitOptions::Mode& mode) {
case WasmSplitOptions::Mode::MergeProfiles:
o << "merge-profiles";
break;
+ case WasmSplitOptions::Mode::PrintProfile:
+ o << "print-profile";
+ break;
}
return o;
}
@@ -105,6 +108,16 @@ WasmSplitOptions::WasmSplitOptions()
[&](Options* o, const std::string& argument) {
mode = Mode::MergeProfiles;
})
+ .add("--print-profile",
+ "",
+ "Print profile contents in a human-readable format.",
+ WasmSplitOption,
+ {Mode::PrintProfile},
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) {
+ mode = Mode::PrintProfile;
+ profileFile = argument;
+ })
.add(
"--profile",
"",
@@ -278,6 +291,12 @@ WasmSplitOptions::WasmSplitOptions()
{Mode::Instrument, Mode::MergeProfiles},
Options::Arguments::One,
[&](Options* o, const std::string& argument) { output = argument; })
+ .add("--unescape",
+ "-u",
+ "Un-escape function names (in print-profile output)",
+ WasmSplitOption,
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { unescape = true; })
.add("--verbose",
"-v",
"Verbose output mode. Prints the functions that will be kept "
@@ -362,6 +381,11 @@ bool WasmSplitOptions::validate() {
case Mode::MergeProfiles:
// Any number >= 1 allowed.
break;
+ case Mode::PrintProfile:
+ if (inputFiles.size() != 1) {
+ fail("Must have exactly one profile path.");
+ }
+ break;
}
// Validate that all used options are allowed in the current mode.
diff --git a/src/tools/wasm-split/split-options.h b/src/tools/wasm-split/split-options.h
index 16e7b75e6..9f6253626 100644
--- a/src/tools/wasm-split/split-options.h
+++ b/src/tools/wasm-split/split-options.h
@@ -28,10 +28,11 @@ struct WasmSplitOptions : ToolOptions {
Split,
Instrument,
MergeProfiles,
+ PrintProfile,
};
Mode mode = Mode::Split;
constexpr static size_t NumModes =
- static_cast<unsigned>(Mode::MergeProfiles) + 1;
+ static_cast<unsigned>(Mode::PrintProfile) + 1;
enum class StorageKind : unsigned {
InGlobals, // Store profile data in WebAssembly Globals
@@ -39,6 +40,7 @@ struct WasmSplitOptions : ToolOptions {
};
StorageKind storageKind = StorageKind::InGlobals;
+ bool unescape = false;
bool verbose = false;
bool emitBinary = true;
bool symbolMap = false;
diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp
index 871d6bc95..43db1b7d2 100644
--- a/src/tools/wasm-split/wasm-split.cpp
+++ b/src/tools/wasm-split/wasm-split.cpp
@@ -17,6 +17,8 @@
// wasm-split: Split a module in two or instrument a module to inform future
// splitting.
+#include <fstream>
+
#include "ir/module-splitting.h"
#include "ir/names.h"
#include "support/file.h"
@@ -149,6 +151,34 @@ ProfileData readProfile(const std::string& file) {
return {hash, timestamps};
}
+void getFunctionsToKeepAndSplit(Module& wasm,
+ uint64_t wasmHash,
+ const std::string& profileFile,
+ std::set<Name>& keepFuncs,
+ std::set<Name>& splitFuncs) {
+ ProfileData profile = readProfile(profileFile);
+ if (profile.hash != wasmHash) {
+ Fatal() << "error: checksum in profile does not match module checksum. "
+ << "The split module must be the original module that was "
+ << "instrumented to generate the profile.";
+ }
+
+ size_t i = 0;
+ ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) {
+ if (i >= profile.timestamps.size()) {
+ Fatal() << "Unexpected end of profile data";
+ }
+ if (profile.timestamps[i++] > 0) {
+ keepFuncs.insert(func->name);
+ } else {
+ splitFuncs.insert(func->name);
+ }
+ });
+ if (i != profile.timestamps.size()) {
+ Fatal() << "Unexpected extra profile data";
+ }
+}
+
void writeSymbolMap(Module& wasm, std::string filename) {
PassOptions options;
options.arguments["symbolmap"] = filename;
@@ -175,24 +205,9 @@ void splitModule(const WasmSplitOptions& options) {
if (options.profileFile.size()) {
// Use the profile to set `keepFuncs`.
uint64_t hash = hashFile(options.inputFiles[0]);
- ProfileData profile = readProfile(options.profileFile);
- if (profile.hash != hash) {
- Fatal() << "error: checksum in profile does not match module checksum. "
- << "The split module must be the original module that was "
- << "instrumented to generate the profile.";
- }
- size_t i = 0;
- ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) {
- if (i >= profile.timestamps.size()) {
- Fatal() << "Unexpected end of profile data";
- }
- if (profile.timestamps[i++] > 0) {
- keepFuncs.insert(func->name);
- }
- });
- if (i != profile.timestamps.size()) {
- Fatal() << "Unexpected extra profile data";
- }
+ std::set<Name> splitFuncs;
+ getFunctionsToKeepAndSplit(
+ wasm, hash, options.profileFile, keepFuncs, splitFuncs);
} else if (options.keepFuncs.size()) {
// Use the explicitly provided `keepFuncs`.
for (auto& func : options.keepFuncs) {
@@ -385,6 +400,61 @@ void mergeProfiles(const WasmSplitOptions& options) {
buffer.writeTo(out.getStream());
}
+std::string unescape(std::string input) {
+ std::string output;
+ for (size_t i = 0; i < input.length(); i++) {
+ if ((input[i] == '\\') && (i + 2 < input.length()) &&
+ isxdigit(input[i + 1]) && isxdigit(input[i + 2])) {
+ std::string byte = input.substr(i + 1, 2);
+ i += 2;
+ char chr = (char)(int)strtol(byte.c_str(), nullptr, 16);
+ output.push_back(chr);
+ } else {
+ output.push_back(input[i]);
+ }
+ }
+ return output;
+}
+
+void checkExists(const std::string& path) {
+ std::ifstream infile(path);
+ if (!infile.is_open()) {
+ Fatal() << "File not found: " << path;
+ }
+}
+
+void printReadableProfile(const WasmSplitOptions& options) {
+ const std::string wasmFile(options.inputFiles[0]);
+ checkExists(options.profileFile);
+ checkExists(wasmFile);
+
+ Module wasm;
+ parseInput(wasm, options);
+
+ std::set<Name> keepFuncs;
+ std::set<Name> splitFuncs;
+
+ uint64_t hash = hashFile(wasmFile);
+ getFunctionsToKeepAndSplit(
+ wasm, hash, options.profileFile, keepFuncs, splitFuncs);
+
+ auto printFnSet = [&](auto funcs, std::string prefix) {
+ for (auto it = funcs.begin(); it != funcs.end(); ++it) {
+ std::cout << prefix << " "
+ << (options.unescape ? unescape(it->c_str()) : it->c_str())
+ << std::endl;
+ }
+ };
+
+ std::cout << "Keeping functions: " << std::endl;
+ printFnSet(keepFuncs, "+");
+ std::cout << std::endl;
+
+ std::cout << "Splitting out functions: " << std::endl;
+ printFnSet(splitFuncs, "-");
+ std::cout << std::endl;
+}
+
} // anonymous namespace
int main(int argc, const char* argv[]) {
@@ -405,5 +475,8 @@ int main(int argc, const char* argv[]) {
case WasmSplitOptions::Mode::MergeProfiles:
mergeProfiles(options);
break;
+ case WasmSplitOptions::Mode::PrintProfile:
+ printReadableProfile(options);
+ break;
}
}