diff options
author | sps-gold <79571312+sps-gold@users.noreply.github.com> | 2022-07-26 03:56:41 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-25 12:56:41 -0700 |
commit | 3a8d28f421a9452aff8f7ed2b140e12e2f314322 (patch) | |
tree | 8c67e20249568785d89796a24780c50640091463 /src | |
parent | 68e2ed11e86e72cc4e12a1b4026ef19d5149fda9 (diff) | |
download | binaryen-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.cpp | 24 | ||||
-rw-r--r-- | src/tools/wasm-split/split-options.h | 4 | ||||
-rw-r--r-- | src/tools/wasm-split/wasm-split.cpp | 109 |
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; } } |