mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:42:43 +00:00 
			
		
		
		
	Profiler: Add a new "Samples" view to the main UI
You can now view the individual samples in a profile one by one with the new "Samples" view. The "old" main view moves into a "Call Tree" tab (but it remains the default view.) When you select a sample in the samples view, we show you the full symbolicated backtrace in a separate view on the right hand side. :^)
This commit is contained in:
		
							parent
							
								
									2c1f71055f
								
							
						
					
					
						commit
						1fb1279cfd
					
				
					 8 changed files with 369 additions and 11 deletions
				
			
		|  | @ -1,9 +1,11 @@ | |||
| set(SOURCES | ||||
|     DisassemblyModel.cpp | ||||
|     main.cpp | ||||
|         IndividualSampleModel.cpp | ||||
|         Profile.cpp | ||||
|     ProfileModel.cpp | ||||
|         ProfileTimelineWidget.cpp | ||||
|         SamplesModel.cpp | ||||
| ) | ||||
| 
 | ||||
| serenity_app(Profiler ICON app-profiler) | ||||
|  |  | |||
							
								
								
									
										91
									
								
								Userland/DevTools/Profiler/IndividualSampleModel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								Userland/DevTools/Profiler/IndividualSampleModel.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2018-2021, 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 "IndividualSampleModel.h" | ||||
| #include "Profile.h" | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| IndividualSampleModel::IndividualSampleModel(Profile& profile, size_t event_index) | ||||
|     : m_profile(profile) | ||||
|     , m_event_index(event_index) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| IndividualSampleModel::~IndividualSampleModel() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| int IndividualSampleModel::row_count(const GUI::ModelIndex&) const | ||||
| { | ||||
|     auto& event = m_profile.events().at(m_event_index); | ||||
|     return event.frames.size(); | ||||
| } | ||||
| 
 | ||||
| int IndividualSampleModel::column_count(const GUI::ModelIndex&) const | ||||
| { | ||||
|     return Column::__Count; | ||||
| } | ||||
| 
 | ||||
| String IndividualSampleModel::column_name(int column) const | ||||
| { | ||||
|     switch (column) { | ||||
|     case Column::Address: | ||||
|         return "Address"; | ||||
|     case Column::ObjectName: | ||||
|         return "Object"; | ||||
|     case Column::Symbol: | ||||
|         return "Symbol"; | ||||
|     default: | ||||
|         VERIFY_NOT_REACHED(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| GUI::Variant IndividualSampleModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const | ||||
| { | ||||
|     auto& event = m_profile.events().at(m_event_index); | ||||
|     auto& frame = event.frames[event.frames.size() - index.row() - 1]; | ||||
| 
 | ||||
|     if (role == GUI::ModelRole::Display) { | ||||
|         if (index.column() == Column::Address) | ||||
|             return String::formatted("{:08x}", frame.address); | ||||
| 
 | ||||
|         if (index.column() == Column::Symbol) { | ||||
|             return frame.symbol; | ||||
|         } | ||||
| 
 | ||||
|         if (index.column() == Column::ObjectName) { | ||||
|             return frame.object_name; | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| void IndividualSampleModel::update() | ||||
| { | ||||
|     did_update(Model::InvalidateAllIndexes); | ||||
| } | ||||
							
								
								
									
										60
									
								
								Userland/DevTools/Profiler/IndividualSampleModel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								Userland/DevTools/Profiler/IndividualSampleModel.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2018-2021, 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> | ||||
| 
 | ||||
| class Profile; | ||||
| 
 | ||||
| class IndividualSampleModel final : public GUI::Model { | ||||
| public: | ||||
|     static NonnullRefPtr<IndividualSampleModel> create(Profile& profile, size_t event_index) | ||||
|     { | ||||
|         return adopt(*new IndividualSampleModel(profile, event_index)); | ||||
|     } | ||||
| 
 | ||||
|     enum Column { | ||||
|         Address, | ||||
|         ObjectName, | ||||
|         Symbol, | ||||
|         __Count | ||||
|     }; | ||||
| 
 | ||||
|     virtual ~IndividualSampleModel() override; | ||||
| 
 | ||||
|     virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; | ||||
|     virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; | ||||
|     virtual String column_name(int) const override; | ||||
|     virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; | ||||
|     virtual void update() override; | ||||
| 
 | ||||
| private: | ||||
|     IndividualSampleModel(Profile&, size_t event_index); | ||||
| 
 | ||||
|     Profile& m_profile; | ||||
|     const size_t m_event_index { 0 }; | ||||
| }; | ||||
|  | @ -27,6 +27,7 @@ | |||
| #include "Profile.h" | ||||
| #include "DisassemblyModel.h" | ||||
| #include "ProfileModel.h" | ||||
| #include "SamplesModel.h" | ||||
| #include <AK/HashTable.h> | ||||
| #include <AK/LexicalPath.h> | ||||
| #include <AK/MappedFile.h> | ||||
|  | @ -55,6 +56,7 @@ Profile::Profile(String executable_path, Vector<Event> events, NonnullOwnPtr<Lib | |||
|     m_last_timestamp = m_events.last().timestamp; | ||||
| 
 | ||||
|     m_model = ProfileModel::create(*this); | ||||
|     m_samples_model = SamplesModel::create(*this); | ||||
| 
 | ||||
|     for (auto& event : m_events) { | ||||
|         m_deepest_stack_depth = max((u32)event.frames.size(), m_deepest_stack_depth); | ||||
|  | @ -72,6 +74,11 @@ GUI::Model& Profile::model() | |||
|     return *m_model; | ||||
| } | ||||
| 
 | ||||
| GUI::Model& Profile::samples_model() | ||||
| { | ||||
|     return *m_samples_model; | ||||
| } | ||||
| 
 | ||||
| void Profile::rebuild_tree() | ||||
| { | ||||
|     u32 filtered_event_count = 0; | ||||
|  | @ -91,18 +98,14 @@ void Profile::rebuild_tree() | |||
| 
 | ||||
|     HashTable<FlatPtr> live_allocations; | ||||
| 
 | ||||
|     for (auto& event : m_events) { | ||||
|         if (has_timestamp_filter_range()) { | ||||
|             auto timestamp = event.timestamp; | ||||
|             if (timestamp < m_timestamp_filter_range_start || timestamp > m_timestamp_filter_range_end) | ||||
|                 continue; | ||||
|         } | ||||
| 
 | ||||
|     for_each_event_in_filter_range([&](auto& event) { | ||||
|         if (event.type == "malloc") | ||||
|             live_allocations.set(event.ptr); | ||||
|         else if (event.type == "free") | ||||
|             live_allocations.remove(event.ptr); | ||||
|     } | ||||
|     }); | ||||
| 
 | ||||
|     Optional<size_t> first_filtered_event_index; | ||||
| 
 | ||||
|     for (size_t event_index = 0; event_index < m_events.size(); ++event_index) { | ||||
|         auto& event = m_events.at(event_index); | ||||
|  | @ -112,6 +115,9 @@ void Profile::rebuild_tree() | |||
|                 continue; | ||||
|         } | ||||
| 
 | ||||
|         if (!first_filtered_event_index.has_value()) | ||||
|             first_filtered_event_index = event_index; | ||||
| 
 | ||||
|         if (event.type == "malloc" && !live_allocations.contains(event.ptr)) | ||||
|             continue; | ||||
| 
 | ||||
|  | @ -197,6 +203,7 @@ void Profile::rebuild_tree() | |||
|     sort_profile_nodes(roots); | ||||
| 
 | ||||
|     m_filtered_event_count = filtered_event_count; | ||||
|     m_first_filtered_event_index = first_filtered_event_index.value_or(0); | ||||
|     m_roots = move(roots); | ||||
|     m_model->update(); | ||||
| } | ||||
|  | @ -307,6 +314,7 @@ void Profile::set_timestamp_filter_range(u64 start, u64 end) | |||
|     m_timestamp_filter_range_end = max(start, end); | ||||
| 
 | ||||
|     rebuild_tree(); | ||||
|     m_samples_model->update(); | ||||
| } | ||||
| 
 | ||||
| void Profile::clear_timestamp_filter_range() | ||||
|  | @ -315,6 +323,7 @@ void Profile::clear_timestamp_filter_range() | |||
|         return; | ||||
|     m_has_timestamp_filter_range = false; | ||||
|     rebuild_tree(); | ||||
|     m_samples_model->update(); | ||||
| } | ||||
| 
 | ||||
| void Profile::set_inverted(bool inverted) | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ | |||
| 
 | ||||
| class ProfileModel; | ||||
| class DisassemblyModel; | ||||
| class SamplesModel; | ||||
| 
 | ||||
| class ProfileNode : public RefCounted<ProfileNode> { | ||||
| public: | ||||
|  | @ -132,6 +133,7 @@ public: | |||
|     ~Profile(); | ||||
| 
 | ||||
|     GUI::Model& model(); | ||||
|     GUI::Model& samples_model(); | ||||
|     GUI::Model* disassembly_model(); | ||||
| 
 | ||||
|     void set_disassembly_index(const GUI::ModelIndex&); | ||||
|  | @ -154,6 +156,7 @@ public: | |||
|         Vector<Frame> frames; | ||||
|     }; | ||||
| 
 | ||||
|     u32 first_filtered_event_index() const { return m_first_filtered_event_index; } | ||||
|     u32 filtered_event_count() const { return m_filtered_event_count; } | ||||
| 
 | ||||
|     const Vector<Event>& events() const { return m_events; } | ||||
|  | @ -200,6 +203,19 @@ public: | |||
| 
 | ||||
|     const LibraryMetadata& libraries() const { return *m_library_metadata; } | ||||
| 
 | ||||
|     template<typename Callback> | ||||
|     void for_each_event_in_filter_range(Callback callback) | ||||
|     { | ||||
|         for (auto& event : m_events) { | ||||
|             if (has_timestamp_filter_range()) { | ||||
|                 auto timestamp = event.timestamp; | ||||
|                 if (timestamp < m_timestamp_filter_range_start || timestamp > m_timestamp_filter_range_end) | ||||
|                     continue; | ||||
|             } | ||||
|             callback(event); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     Profile(String executable_path, Vector<Event>, NonnullOwnPtr<LibraryMetadata>); | ||||
| 
 | ||||
|  | @ -208,12 +224,14 @@ private: | |||
|     String m_executable_path; | ||||
| 
 | ||||
|     RefPtr<ProfileModel> m_model; | ||||
|     RefPtr<SamplesModel> m_samples_model; | ||||
|     RefPtr<DisassemblyModel> m_disassembly_model; | ||||
| 
 | ||||
|     GUI::ModelIndex m_disassembly_index; | ||||
| 
 | ||||
|     Vector<NonnullRefPtr<ProfileNode>> m_roots; | ||||
|     u32 m_filtered_event_count { 0 }; | ||||
|     size_t m_first_filtered_event_index { 0 }; | ||||
|     u64 m_first_timestamp { 0 }; | ||||
|     u64 m_last_timestamp { 0 }; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										95
									
								
								Userland/DevTools/Profiler/SamplesModel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								Userland/DevTools/Profiler/SamplesModel.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2018-2021, 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 "SamplesModel.h" | ||||
| #include "Profile.h" | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| SamplesModel::SamplesModel(Profile& profile) | ||||
|     : m_profile(profile) | ||||
| { | ||||
|     m_user_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png")); | ||||
|     m_kernel_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object-red.png")); | ||||
| } | ||||
| 
 | ||||
| SamplesModel::~SamplesModel() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| int SamplesModel::row_count(const GUI::ModelIndex&) const | ||||
| { | ||||
|     return m_profile.filtered_event_count(); | ||||
| } | ||||
| 
 | ||||
| int SamplesModel::column_count(const GUI::ModelIndex&) const | ||||
| { | ||||
|     return Column::__Count; | ||||
| } | ||||
| 
 | ||||
| String SamplesModel::column_name(int column) const | ||||
| { | ||||
|     switch (column) { | ||||
|     case Column::SampleIndex: | ||||
|         return "#"; | ||||
|     case Column::Timestamp: | ||||
|         return "Timestamp"; | ||||
|     case Column::InnermostStackFrame: | ||||
|         return "Innermost Frame"; | ||||
|     default: | ||||
|         VERIFY_NOT_REACHED(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| GUI::Variant SamplesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const | ||||
| { | ||||
|     u32 event_index = m_profile.first_filtered_event_index() + index.row(); | ||||
|     auto& event = m_profile.events().at(event_index); | ||||
| 
 | ||||
|     if (role == GUI::ModelRole::Custom) { | ||||
|         return event_index; | ||||
|     } | ||||
| 
 | ||||
|     if (role == GUI::ModelRole::Display) { | ||||
|         if (index.column() == Column::SampleIndex) | ||||
|             return event_index; | ||||
| 
 | ||||
|         if (index.column() == Column::Timestamp) { | ||||
|             return (u32)event.timestamp; | ||||
|         } | ||||
| 
 | ||||
|         if (index.column() == Column::InnermostStackFrame) { | ||||
|             return event.frames.last().symbol; | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| void SamplesModel::update() | ||||
| { | ||||
|     did_update(Model::InvalidateAllIndexes); | ||||
| } | ||||
							
								
								
									
										62
									
								
								Userland/DevTools/Profiler/SamplesModel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								Userland/DevTools/Profiler/SamplesModel.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2018-2021, 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> | ||||
| 
 | ||||
| class Profile; | ||||
| 
 | ||||
| class SamplesModel final : public GUI::Model { | ||||
| public: | ||||
|     static NonnullRefPtr<SamplesModel> create(Profile& profile) | ||||
|     { | ||||
|         return adopt(*new SamplesModel(profile)); | ||||
|     } | ||||
| 
 | ||||
|     enum Column { | ||||
|         SampleIndex, | ||||
|         Timestamp, | ||||
|         InnermostStackFrame, | ||||
|         __Count | ||||
|     }; | ||||
| 
 | ||||
|     virtual ~SamplesModel() override; | ||||
| 
 | ||||
|     virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; | ||||
|     virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; | ||||
|     virtual String column_name(int) const override; | ||||
|     virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; | ||||
|     virtual void update() override; | ||||
| 
 | ||||
| private: | ||||
|     explicit SamplesModel(Profile&); | ||||
| 
 | ||||
|     Profile& m_profile; | ||||
| 
 | ||||
|     GUI::Icon m_user_frame_icon; | ||||
|     GUI::Icon m_kernel_frame_icon; | ||||
| }; | ||||
|  | @ -24,6 +24,7 @@ | |||
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| 
 | ||||
| #include "IndividualSampleModel.h" | ||||
| #include "Profile.h" | ||||
| #include "ProfileTimelineWidget.h" | ||||
| #include <LibCore/ArgsParser.h> | ||||
|  | @ -44,6 +45,7 @@ | |||
| #include <LibGUI/Model.h> | ||||
| #include <LibGUI/ProcessChooser.h> | ||||
| #include <LibGUI/Splitter.h> | ||||
| #include <LibGUI/TabWidget.h> | ||||
| #include <LibGUI/TableView.h> | ||||
| #include <LibGUI/TreeView.h> | ||||
| #include <LibGUI/Window.h> | ||||
|  | @ -100,7 +102,12 @@ int main(int argc, char** argv) | |||
| 
 | ||||
|     main_widget.add<ProfileTimelineWidget>(*profile); | ||||
| 
 | ||||
|     auto& bottom_splitter = main_widget.add<GUI::VerticalSplitter>(); | ||||
|     auto& tab_widget = main_widget.add<GUI::TabWidget>(); | ||||
| 
 | ||||
|     auto& tree_tab = tab_widget.add_tab<GUI::Widget>("Call Tree"); | ||||
|     tree_tab.set_layout<GUI::VerticalBoxLayout>(); | ||||
|     tree_tab.layout()->set_margins({ 4, 4, 4, 4 }); | ||||
|     auto& bottom_splitter = tree_tab.add<GUI::VerticalSplitter>(); | ||||
| 
 | ||||
|     auto& tree_view = bottom_splitter.add<GUI::TreeView>(); | ||||
|     tree_view.set_should_fill_selected_rows(true); | ||||
|  | @ -114,6 +121,20 @@ int main(int argc, char** argv) | |||
|         disassembly_view.set_model(profile->disassembly_model()); | ||||
|     }; | ||||
| 
 | ||||
|     auto& samples_tab = tab_widget.add_tab<GUI::Widget>("Samples"); | ||||
|     samples_tab.set_layout<GUI::VerticalBoxLayout>(); | ||||
|     samples_tab.layout()->set_margins({ 4, 4, 4, 4 }); | ||||
| 
 | ||||
|     auto& samples_splitter = samples_tab.add<GUI::HorizontalSplitter>(); | ||||
|     auto& samples_table_view = samples_splitter.add<GUI::TableView>(); | ||||
|     samples_table_view.set_model(profile->samples_model()); | ||||
| 
 | ||||
|     auto& individual_sample_view = samples_splitter.add<GUI::TableView>(); | ||||
|     samples_table_view.on_selection = [&](const GUI::ModelIndex& index) { | ||||
|         auto model = IndividualSampleModel::create(*profile, index.data(GUI::ModelRole::Custom).to_integer<size_t>()); | ||||
|         individual_sample_view.set_model(move(model)); | ||||
|     }; | ||||
| 
 | ||||
|     auto menubar = GUI::MenuBar::construct(); | ||||
|     auto& app_menu = menubar->add_menu("Profiler"); | ||||
|     app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling