diff options
-rw-r--r-- | DevTools/Profiler/CMakeLists.txt | 1 | ||||
-rw-r--r-- | DevTools/Profiler/RunningProcessesModel.cpp | 122 | ||||
-rw-r--r-- | DevTools/Profiler/RunningProcessesModel.h | 65 | ||||
-rw-r--r-- | DevTools/Profiler/main.cpp | 112 |
4 files changed, 295 insertions, 5 deletions
diff --git a/DevTools/Profiler/CMakeLists.txt b/DevTools/Profiler/CMakeLists.txt index 9e224d3ae4..a0d0bd9624 100644 --- a/DevTools/Profiler/CMakeLists.txt +++ b/DevTools/Profiler/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES Profile.cpp ProfileModel.cpp ProfileTimelineWidget.cpp + RunningProcessesModel.cpp ) serenity_bin(Profiler) diff --git a/DevTools/Profiler/RunningProcessesModel.cpp b/DevTools/Profiler/RunningProcessesModel.cpp new file mode 100644 index 0000000000..dbea3a26a1 --- /dev/null +++ b/DevTools/Profiler/RunningProcessesModel.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "RunningProcessesModel.h" +#include <AK/SharedBuffer.h> +#include <LibCore/ProcessStatisticsReader.h> + +namespace Profiler { + +NonnullRefPtr<RunningProcessesModel> RunningProcessesModel::create() +{ + return adopt(*new RunningProcessesModel); +} + +RunningProcessesModel::RunningProcessesModel() +{ +} + +RunningProcessesModel::~RunningProcessesModel() +{ +} + +void RunningProcessesModel::update() +{ + m_processes.clear(); + + Core::ProcessStatisticsReader reader; + auto processes = reader.get_all(); + + for (auto& it : processes) { + Process process; + process.pid = it.value.pid; + process.uid = it.value.uid; + if (it.value.icon_id != -1) { + if (auto icon_buffer = SharedBuffer::create_from_shbuf_id(it.value.icon_id)) { + process.icon = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *icon_buffer, { 16, 16 }); + } + } + process.name = it.value.name; + + m_processes.append(move(process)); + } + + did_update(); +} + +int RunningProcessesModel::row_count(const GUI::ModelIndex&) const +{ + return m_processes.size(); +} + +int RunningProcessesModel::column_count(const GUI::ModelIndex&) const +{ + return Column::__Count; +} + +String RunningProcessesModel::column_name(int column_index) const +{ + switch (column_index) { + case Column::Icon: + return {}; + case Column::PID: + return "PID"; + case Column::UID: + return "UID"; + case Column::Name: + return "Name"; + } + ASSERT_NOT_REACHED(); +} + +GUI::Variant RunningProcessesModel::data(const GUI::ModelIndex& index, Role role) const +{ + auto& process = m_processes[index.row()]; + + if (role == Role::Custom) { + return process.pid; + } + + if (role == Role::Display) { + switch (index.column()) { + case Column::Icon: + if (!process.icon) + return GUI::Icon(); + return GUI::Icon(*process.icon); + case Column::PID: + return process.pid; + case Column::UID: + return process.uid; + case Column::Name: + return process.name; + } + ASSERT_NOT_REACHED(); + } + + return {}; +} + +} diff --git a/DevTools/Profiler/RunningProcessesModel.h b/DevTools/Profiler/RunningProcessesModel.h new file mode 100644 index 0000000000..3984fab3eb --- /dev/null +++ b/DevTools/Profiler/RunningProcessesModel.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibGUI/Model.h> +#include <LibGfx/Bitmap.h> + +namespace Profiler { + +class RunningProcessesModel final : public GUI::Model { +public: + static NonnullRefPtr<RunningProcessesModel> create(); + virtual ~RunningProcessesModel() override; + + enum Column { + Icon, + PID, + UID, + Name, + __Count, + }; + + virtual int row_count(const GUI::ModelIndex&) const override; + virtual int column_count(const GUI::ModelIndex&) const override; + virtual String column_name(int column_index) const override; + virtual GUI::Variant data(const GUI::ModelIndex&, Role = Role::Display) const override; + virtual void update() override; + +private: + RunningProcessesModel(); + + struct Process { + pid_t pid; + uid_t uid; + RefPtr<Gfx::Bitmap> icon; + String name; + }; + Vector<Process> m_processes; +}; + +} diff --git a/DevTools/Profiler/main.cpp b/DevTools/Profiler/main.cpp index dc64030101..480d0116e0 100644 --- a/DevTools/Profiler/main.cpp +++ b/DevTools/Profiler/main.cpp @@ -26,26 +26,46 @@ #include "Profile.h" #include "ProfileTimelineWidget.h" +#include "RunningProcessesModel.h" +#include <LibCore/ElapsedTimer.h> +#include <LibCore/EventLoop.h> +#include <LibCore/Timer.h> #include <LibGUI/Action.h> #include <LibGUI/Application.h> #include <LibGUI/BoxLayout.h> +#include <LibGUI/Button.h> +#include <LibGUI/Desktop.h> +#include <LibGUI/Label.h> #include <LibGUI/Menu.h> #include <LibGUI/MenuBar.h> +#include <LibGUI/MessageBox.h> #include <LibGUI/Model.h> +#include <LibGUI/SortingProxyModel.h> #include <LibGUI/Splitter.h> #include <LibGUI/TableView.h> #include <LibGUI/TreeView.h> #include <LibGUI/Window.h> +#include <serenity.h> #include <stdio.h> +#include <string.h> + +static bool generate_profile(); int main(int argc, char** argv) { + GUI::Application app(argc, argv); + + const char* path = nullptr; if (argc != 2) { - printf("usage: %s <profile-file>\n", argv[0]); - return 0; + if (!generate_profile()) + return 0; + path = "/proc/profile"; + } else { + path = argv[1]; } - const char* path = argv[1]; + printf("We're here!\n"); + auto profile = Profile::load_from_perfcore_file(path); if (!profile) { @@ -53,8 +73,6 @@ int main(int argc, char** argv) return 1; } - GUI::Application app(argc, argv); - auto window = GUI::Window::construct(); window->set_title("Profiler"); window->set_rect(100, 100, 800, 600); @@ -102,3 +120,87 @@ int main(int argc, char** argv) window->show(); return app.exec(); } + +bool prompt_for_process_to_profile() +{ + bool success = false; + auto window = GUI::Window::construct(); + window->set_title("Profiler"); + Gfx::IntRect window_rect { 0, 0, 480, 360 }; + window_rect.center_within(GUI::Desktop::the().rect()); + window->set_rect(window_rect); + auto& widget = window->set_main_widget<GUI::Widget>(); + widget.set_fill_with_background_color(true); + widget.set_layout<GUI::VerticalBoxLayout>(); + auto& table_view = widget.add<GUI::TableView>(); + table_view.set_model(GUI::SortingProxyModel::create(Profiler::RunningProcessesModel::create())); + table_view.model()->set_key_column_and_sort_order(Profiler::RunningProcessesModel::Column::PID, GUI::SortOrder::Descending); + auto& button_container = widget.add<GUI::Widget>(); + button_container.set_preferred_size(0, 30); + button_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); + button_container.set_layout<GUI::HorizontalBoxLayout>(); + auto& profile_button = button_container.add<GUI::Button>("Profile"); + profile_button.on_click = [&](auto) { + if (table_view.selection().is_empty()) { + GUI::MessageBox::show("No process selected!", "Profiler", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, window); + return; + } + auto index = table_view.selection().first(); + auto pid_as_variant = table_view.model()->data(index, GUI::Model::Role::Custom); + auto pid = pid_as_variant.as_i32(); + if (profiling_enable(pid) < 0) { + int saved_errno = errno; + GUI::MessageBox::show(String::format("Unable to profile PID %d: %s", pid, strerror(saved_errno)), "Profiler", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, window); + return; + } + + success = true; + GUI::Application::the().quit(0); + }; + auto& cancel_button = button_container.add<GUI::Button>("Cancel"); + cancel_button.on_click = [](auto) { + GUI::Application::the().quit(1); + }; + + table_view.model()->update(); + window->show(); + return GUI::Application::the().exec() == 0; +} + +bool prompt_to_stop_profiling() +{ + auto window = GUI::Window::construct(); + window->set_title("Profiling"); + Gfx::IntRect window_rect { 0, 0, 320, 200 }; + window_rect.center_within(GUI::Desktop::the().rect()); + window->set_rect(window_rect); + auto& widget = window->set_main_widget<GUI::Widget>(); + widget.set_fill_with_background_color(true); + widget.set_layout<GUI::VerticalBoxLayout>(); + + auto& timer_label = widget.add<GUI::Label>("..."); + Core::ElapsedTimer clock; + clock.start(); + auto update_timer = Core::Timer::construct(100, [&] { + timer_label.set_text(String::format("%.1f seconds", (float)clock.elapsed() / 1000.0f)); + }); + + auto& stop_button = widget.add<GUI::Button>("Stop"); + stop_button.on_click = [&](auto) { + GUI::Application::the().quit(); + }; + + window->show(); + return GUI::Application::the().exec() == 0; +} + +bool generate_profile() +{ + if (!prompt_for_process_to_profile()) + return false; + + if (!prompt_to_stop_profiling()) + return false; + + return true; +} |