1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 19:05:07 +00:00
serenity/Userland/DevTools/UserspaceEmulator/main.cpp
Brian Gianforcaro ad1051089c UserspaceEmulator: Profiles are now expected to have a strings array
The kernel profiles were recently changed to have a `strings` array
as part of the profile objects. The `ProfileViewer` now checks for
that during startup and declares the profile invalid if the array
is not present.

The UserspaceEmulator doesn't use the API which the kernel exposed
the string array for, so just fake it by always adding an empty array
to the generated profiles.
2021-08-28 20:03:08 +02:00

116 lines
4.1 KiB
C++

/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Emulator.h"
#include <AK/FileStream.h>
#include <AK/Format.h>
#include <AK/LexicalPath.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <fcntl.h>
#include <pthread.h>
#include <serenity.h>
#include <string.h>
bool g_report_to_debug = false;
int main(int argc, char** argv, char** env)
{
Vector<String> arguments;
bool pause_on_startup { false };
String profile_dump_path;
FILE* profile_output_file { nullptr };
bool enable_roi_mode { false };
bool dump_profile { false };
unsigned profile_instruction_interval { 0 };
Core::ArgsParser parser;
parser.set_stop_on_first_non_option(true);
parser.add_option(g_report_to_debug, "Write reports to the debug log", "report-to-debug", 0);
parser.add_option(pause_on_startup, "Pause on startup", "pause", 'p');
parser.add_option(dump_profile, "Generate a ProfileViewer-compatible profile", "profile", 0);
parser.add_option(profile_instruction_interval, "Set the profile instruction capture interval, 128 by default", "profile-interval", 'i', "#instructions");
parser.add_option(profile_dump_path, "File path for profile dump", "profile-file", 0, "path");
parser.add_option(enable_roi_mode, "Enable Region-of-Interest mode for profiling", "roi", 0);
parser.add_positional_argument(arguments, "Command to emulate", "command");
parser.parse(argc, argv);
if (dump_profile && profile_instruction_interval == 0)
profile_instruction_interval = 128;
String executable_path;
if (arguments[0].contains("/"sv))
executable_path = Core::File::real_path_for(arguments[0]);
else
executable_path = Core::find_executable_in_path(arguments[0]);
if (executable_path.is_empty()) {
reportln("Cannot find executable for '{}'.", arguments[0]);
return 1;
}
if (dump_profile && profile_dump_path.is_empty())
profile_dump_path = String::formatted("{}.{}.profile", executable_path, getpid());
OwnPtr<OutputFileStream> profile_stream;
if (dump_profile) {
profile_output_file = fopen(profile_dump_path.characters(), "w+");
if (profile_output_file == nullptr) {
auto error_string = strerror(errno);
warnln("Failed to open '{}' for writing: {}", profile_dump_path, error_string);
return 1;
}
profile_stream = make<OutputFileStream>(profile_output_file);
profile_stream->write_or_error(R"({"events":[)"sv.bytes());
timeval tv {};
gettimeofday(&tv, nullptr);
profile_stream->write_or_error(
String::formatted(
R"~({{"type": "process_create", "parent_pid": 1, "executable": "{}", "pid": {}, "tid": {}, "timestamp": {}, "lost_samples": 0, "stack": []}})~",
executable_path, getpid(), gettid(), tv.tv_sec * 1000 + tv.tv_usec / 1000)
.bytes());
}
Vector<String> environment;
for (int i = 0; env[i]; ++i) {
environment.append(env[i]);
}
// FIXME: It might be nice to tear down the emulator properly.
auto& emulator = *new UserspaceEmulator::Emulator(executable_path, arguments, environment);
emulator.set_profiling_details(dump_profile, profile_instruction_interval, profile_stream);
emulator.set_in_region_of_interest(!enable_roi_mode);
if (!emulator.load_elf())
return 1;
StringBuilder builder;
builder.append("(UE) ");
builder.append(LexicalPath::basename(arguments[0]));
if (set_process_name(builder.string_view().characters_without_null_termination(), builder.string_view().length()) < 0) {
perror("set_process_name");
return 1;
}
int rc = pthread_setname_np(pthread_self(), builder.to_string().characters());
if (rc != 0) {
reportln("pthread_setname_np: {}", strerror(rc));
return 1;
}
if (pause_on_startup)
emulator.pause();
rc = emulator.exec();
if (dump_profile)
emulator.profile_stream().write_or_error(R"(], "strings": []})"sv.bytes());
return rc;
}