diff --git a/explorer/process.h b/explorer/process.h index 4ed652d5d..3c35ff3bc 100644 --- a/explorer/process.h +++ b/explorer/process.h @@ -42,7 +42,12 @@ namespace micro_profiler unsigned int cycle; }; - class process_explorer : public sdb::table + namespace tables + { + typedef sdb::table processes; + } + + class process_explorer : public tables::processes { public: process_explorer(mt::milliseconds update_interval, scheduler::queue &apartment_queue); diff --git a/explorer/src/process_win32.cpp b/explorer/src/process_win32.cpp index 8ab7c86bc..c71210756 100644 --- a/explorer/src/process_win32.cpp +++ b/explorer/src/process_win32.cpp @@ -87,6 +87,7 @@ namespace micro_profiler rec.commit(); } } + invalidate(); _apartment.schedule([this] { update(); }, _update_interval); } } diff --git a/explorer/tests/ProcessExplorerTests.cpp b/explorer/tests/ProcessExplorerTests.cpp index c5818993c..64f794972 100644 --- a/explorer/tests/ProcessExplorerTests.cpp +++ b/explorer/tests/ProcessExplorerTests.cpp @@ -149,6 +149,8 @@ namespace micro_profiler { // INIT process_explorer e(mt::milliseconds(1), queue); + auto invalidations = 0; + auto conn = e.invalidate += [&] { invalidations++; }; auto &idx = sdb::multi_index(e, pid()); shared_ptr child1 = create_process("./guinea_runner", " \"" + controller_id + "\""); @@ -164,6 +166,7 @@ namespace micro_profiler queue.run_one(); // ASSERT + assert_equal(1, invalidations); size_t n_children[5] = { 0 }; assert_equal(1u, queue.tasks.size()); assert_equal(mt::milliseconds(1), queue.tasks.back().second); @@ -180,6 +183,7 @@ namespace micro_profiler queue.run_one(); // ASSERT + assert_equal(2, invalidations); assert_equal(1u, queue.tasks.size()); assert_equal(mt::milliseconds(1), queue.tasks.back().second); assert_equal(n_children[0], count(idx.equal_range(child1->get_pid()))); diff --git a/frontend/columns_layout.h b/frontend/columns_layout.h index a9770271d..0585384e6 100644 --- a/frontend/columns_layout.h +++ b/frontend/columns_layout.h @@ -25,9 +25,13 @@ namespace micro_profiler { struct call_statistics; + struct process_info; + struct process_model_context; struct statistics_model_context; extern const column_definition c_caller_statistics_columns[9]; extern const column_definition c_statistics_columns[9]; extern const column_definition c_callee_statistics_columns[9]; + + extern const column_definition c_processes_columns[3]; } diff --git a/frontend/constructors.h b/frontend/constructors.h index dc17512c8..1eb3647ed 100644 --- a/frontend/constructors.h +++ b/frontend/constructors.h @@ -56,6 +56,13 @@ namespace micro_profiler return std::shared_ptr(p, &p->second); } + template + inline T initialize() + { + T value = { }; + return value; + } + template inline T initialize(const F1 &field1) { diff --git a/frontend/headers_model.h b/frontend/headers_model.h index 03be9a37c..63415ef95 100644 --- a/frontend/headers_model.h +++ b/frontend/headers_model.h @@ -21,7 +21,6 @@ #pragma once #include "column_definition.h" -#include "model_context.h" #include #include @@ -34,11 +33,8 @@ namespace micro_profiler class headers_model : public wpl::headers_model { public: - typedef column_definition column; - - public: - template - headers_model(const column (&columns)[N], index_type sort_column, bool sort_ascending); + template + headers_model(T (&columns)[N], index_type sort_column, bool sort_ascending); void store(hive &configuration) const; void update(const hive &configuration); @@ -52,6 +48,19 @@ namespace micro_profiler virtual void get_caption(index_type index, agge::richtext_t &caption) const override; virtual void activate_column(index_type column_) override; + private: + struct column + { + template + column(const column_definition &from); + + std::string id; + agge::richtext_modifier_t caption; + short int width; + agge::text_alignment alignment; + bool compare, ascending; + }; + private: std::vector _columns; index_type _sort_column; @@ -60,8 +69,21 @@ namespace micro_profiler - template - inline headers_model::headers_model(const column (&columns)[N], index_type sort_column, bool sort_ascending) + template + inline headers_model::column::column(const column_definition &from) + : caption(agge::style_modifier::empty) + { + id = from.id; + caption = from.caption; + width = from.width; + alignment = from.alignment; + compare = !!from.compare; + ascending = from.ascending; + } + + + template + inline headers_model::headers_model(T (&columns)[N], index_type sort_column, bool sort_ascending) : _columns(columns, columns + N), _sort_column(sort_column), _sort_ascending(sort_ascending) { } } diff --git a/frontend/model_context.h b/frontend/model_context.h index a98754368..2e47682d1 100644 --- a/frontend/model_context.h +++ b/frontend/model_context.h @@ -37,4 +37,8 @@ namespace micro_profiler std::shared_ptr resolver; bool canonical; }; + + struct process_model_context + { + }; } diff --git a/frontend/process_list.h b/frontend/process_list.h index cef7f5598..07df04c41 100644 --- a/frontend/process_list.h +++ b/frontend/process_list.h @@ -20,35 +20,35 @@ #pragma once -#include -#include +#include "constructors.h" +#include "model_context.h" +#include "table_model_impl.h" + +#include #include namespace micro_profiler { - class process_list : public wpl::richtext_table_model + template <> + struct key_traits { - public: - typedef std::function process_enumerator_t; - - public: - void update(const process_enumerator_t &enumerator); - - std::shared_ptr get_process(index_type row) const; - void set_order(index_type column, bool ascending); + typedef id_t key_type; - virtual index_type get_count() const throw() override; - virtual void get_text(index_type row, index_type column, agge::richtext_t &text) const override; + static key_type get_key(const process_info &item) + { return item.pid; } + }; - private: - typedef std::vector< std::shared_ptr > process_container_t; + template + inline std::shared_ptr< table_model_impl > process_list( + std::shared_ptr underlying, const ColumnsT &columns) + { + typedef table_model_impl model_type; + typedef std::tuple, wpl::slot_connection> composite_t; - private: - template - void init_sorter(const PredicateT &p); + const auto m = std::make_shared(underlying, initialize()); + const auto c = std::make_shared(m, underlying->invalidate += [m] { m->fetch(); }); - private: - std::vector< std::shared_ptr > _processes; - std::function _sorter; - }; + m->add_columns(columns); + return std::shared_ptr(c, std::get<0>(*c).get()); + } } diff --git a/frontend/src/CMakeLists.txt b/frontend/src/CMakeLists.txt index c085a26d0..aad90a66b 100644 --- a/frontend/src/CMakeLists.txt +++ b/frontend/src/CMakeLists.txt @@ -9,7 +9,6 @@ set(FRONTEND_LIB_SOURCES frontend_patcher.cpp headers_model.cpp image_patch_model.cpp - process_list.cpp profiling_preferences.cpp representation.cpp symbol_resolver.cpp diff --git a/frontend/src/columns_layout.cpp b/frontend/src/columns_layout.cpp index 65c25c460..2aafdd1ec 100644 --- a/frontend/src/columns_layout.cpp +++ b/frontend/src/columns_layout.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include #include @@ -172,6 +174,32 @@ namespace micro_profiler template inline format_integer_ format_integer(const T &underlying) { return initialize< format_integer_ >(underlying); } + + + auto by_process_name = [] (const process_model_context &, const process_info &lhs, const process_info &rhs) { + return strcmp((micro_profiler::operator*)(lhs.path), (micro_profiler::operator*)(rhs.path)); + }; + + auto by_process_pid = [] (const process_model_context &, const process_info &lhs, const process_info &rhs) { + return micro_profiler::compare(lhs.pid, rhs.pid); + }; + + auto by_process_ppid = [] (const process_model_context &, const process_info &lhs, const process_info &rhs) { + return micro_profiler::compare(lhs.parent_pid, rhs.parent_pid); + }; + + + auto process_name = [] (agge::richtext_t &text, const process_model_context &, size_t, const process_info &item) { + text << (micro_profiler::operator*)(item.path); + }; + + auto process_pid = [] (agge::richtext_t &text, const process_model_context &, size_t, const process_info &item) { + micro_profiler::itoa<10>(text, item.pid); + }; + + auto process_ppid = [] (agge::richtext_t &text, const process_model_context &, size_t, const process_info &item) { + micro_profiler::itoa<10>(text, item.parent_pid); + }; } @@ -210,4 +238,11 @@ namespace micro_profiler c_statistics_columns[7], c_statistics_columns[8], }; + + + const column_definition c_processes_columns[] = { + { "ProcessExe", "Process\n" + secondary + "executable", 384, agge::align_near, process_name, by_process_name, true, }, + { "ProcessID", "PID" + secondary, 50, agge::align_far, process_pid, by_process_pid, true, }, + { "ParentProcessID", "PID\n" + secondary + "parent", 50, agge::align_far, process_ppid, by_process_ppid, true, }, + }; } diff --git a/frontend/src/frontend.lib.vcxproj b/frontend/src/frontend.lib.vcxproj index 91d32e329..5a6186cc1 100644 --- a/frontend/src/frontend.lib.vcxproj +++ b/frontend/src/frontend.lib.vcxproj @@ -48,7 +48,6 @@ - diff --git a/frontend/src/frontend.lib.vcxproj.filters b/frontend/src/frontend.lib.vcxproj.filters index 35e8ebfb8..f7ea00c93 100644 --- a/frontend/src/frontend.lib.vcxproj.filters +++ b/frontend/src/frontend.lib.vcxproj.filters @@ -7,9 +7,6 @@ src - - src - src\untested diff --git a/frontend/src/process_list.cpp b/frontend/src/process_list.cpp deleted file mode 100644 index 011c273d4..000000000 --- a/frontend/src/process_list.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2011-2022 by Artem A. Gevorkyan (gevorkyan.org) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include - -#include -#include - -using namespace std; - -namespace micro_profiler -{ - void process_list::update(const process_enumerator_t &enumerator) - { - _processes.clear(); - enumerator([this] (const shared_ptr &p) { - _processes.push_back(p); - }); - if (_sorter) - _sorter(_processes); - invalidate(npos()); - } - - shared_ptr process_list::get_process(index_type row) const - { return _processes[row]; } - - void process_list::set_order(index_type column, bool ascending) - { - if ((0 == column) & ascending) - init_sorter([] (const shared_ptr &lhs, const shared_ptr &rhs) { - return lhs->name() < rhs->name(); - }); - else if ((0 == column) & !ascending) - init_sorter([] (const shared_ptr &lhs, const shared_ptr &rhs) { - return lhs->name() > rhs->name(); - }); - else if ((1 == column) & ascending) - init_sorter([] (const shared_ptr &lhs, const shared_ptr &rhs) { - return lhs->get_pid() < rhs->get_pid(); - }); - else if ((1 == column) & !ascending) - init_sorter([] (const shared_ptr &lhs, const shared_ptr &rhs) { - return lhs->get_pid() > rhs->get_pid(); - }); - _sorter(_processes); - } - - process_list::index_type process_list::get_count() const throw() - { return _processes.size(); } - - void process_list::get_text(index_type row, index_type column, agge::richtext_t &text) const - { - process &p = *_processes[row]; - - switch (column) - { - case 0: text << p.name().c_str(); break; - case 1: itoa<10>(text, p.get_pid()); break; - } - } - - template - void process_list::init_sorter(const PredicateT &p) - { - _sorter = [p] (process_container_t &processes) { - sort(processes.begin(), processes.end(), p); - }; - } -} diff --git a/frontend/src/untested/image_patch_ui.cpp b/frontend/src/untested/image_patch_ui.cpp index a75bec71c..88ac3e968 100644 --- a/frontend/src/untested/image_patch_ui.cpp +++ b/frontend/src/untested/image_patch_ui.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -18,7 +19,7 @@ namespace micro_profiler const auto dummy_get = [] (agge::richtext_t &, const statistics_model_context &, size_t, const call_statistics &) {}; const auto dummy_compare = [] (const statistics_model_context &, const call_statistics &, const call_statistics &) { return false; }; - const headers_model::column c_columns_symbols[] = { + const column_definition c_columns_symbols[] = { { "Rva", "RVA" + secondary, 28, agge::align_far, dummy_get, dummy_compare, true, }, { "Function", "Function\n" + secondary + "qualified name", 384, agge::align_near, dummy_get, dummy_compare, true, }, { "Status", "Profiling\n" + secondary + "status", 64, agge::align_near, dummy_get, dummy_compare, false, }, diff --git a/frontend/tests/HeadersModelTests.cpp b/frontend/tests/HeadersModelTests.cpp index a799211a4..03e6424f5 100644 --- a/frontend/tests/HeadersModelTests.cpp +++ b/frontend/tests/HeadersModelTests.cpp @@ -22,10 +22,11 @@ namespace micro_profiler { namespace { - const auto dummy_get = [] (agge::richtext_t &, const statistics_model_context &, size_t, const call_statistics &) {}; - const auto dummy_compare = [] (const statistics_model_context &, const call_statistics &, const call_statistics &) { return false; }; + const auto dummy_get = [] (agge::richtext_t &/*text*/, ...) {}; + const auto dummy_compare = [] (...) { return false; }; typedef vector< pair > log_t; + typedef column_definition column_t; void append_log(log_t *log, headers_model::index_type sort_column, bool sort_ascending) { log->push_back(make_pair(sort_column, sort_ascending)); } @@ -114,7 +115,7 @@ namespace micro_profiler test( ColumnsModelIsInitiallyOrderedAcordinglyToConstructionParam ) { // INIT - headers_model::column columns[] = { + column_t columns[] = { { "id1", T(""), }, { "id2", T(""), 0, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T(""), 0, agge::align_near, dummy_get, dummy_compare, false, }, @@ -147,7 +148,7 @@ namespace micro_profiler test( FirstOrderingIsDoneAccordinglyToDefaults ) { // INIT - headers_model::column columns[] = { + column_t columns[] = { { "id1", T(""), }, { "id2", T(""), 0, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T(""), 0, agge::align_near, dummy_get, dummy_compare, true, }, @@ -196,7 +197,7 @@ namespace micro_profiler test( SubsequentOrderingReversesTheColumnsState ) { // INIT - headers_model::column columns[] = { + column_t columns[] = { { "id1", T(""), }, { "id2", T(""), 0, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T(""), 0, agge::align_near, dummy_get, dummy_compare, true, }, @@ -237,13 +238,13 @@ namespace micro_profiler test( GettingColumnsCount ) { // INIT - headers_model::column columns1[] = { + column_t columns1[] = { { "id1", T("first"), }, { "id2", T("second"), 0, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T("third"), 0, agge::align_near, dummy_get, dummy_compare, true, }, { "id4", T("fourth"), 0, agge::align_near, dummy_get, dummy_compare, true, }, }; - headers_model::column columns2[] = { + column_t columns2[] = { { "id1", T("a first column"), }, { "id2", T("a second column"), 0, agge::align_near, dummy_get, dummy_compare, true, }, }; @@ -259,12 +260,12 @@ namespace micro_profiler test( GetColumnItem ) { // INIT - headers_model::column columns1[] = { + column_t columns1[] = { { "id1", "first" + style::weight(regular), 0, agge::align_near, }, { "id2", "second" + style::weight(semi_bold) + "appendix", 0, agge::align_far, dummy_get, dummy_compare, false, }, { "id3", "third" + style::weight(regular), 0, agge::align_center, dummy_get, dummy_compare, true, }, }; - headers_model::column columns2[] = { + column_t columns2[] = { { "id1", "a first " + style::family("verdana") + "column", }, { "id2", "a second column" + style::weight(regular), 0, agge::align_near, dummy_get, dummy_compare, true, }, }; @@ -345,7 +346,7 @@ namespace micro_profiler test( ModelUpdateDoesChangeColumnsWidth ) { // INIT - headers_model::column columns[] = { + column_t columns[] = { { "id1", T("first"), }, { "id2", T("second"), 0, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T("third"), 0, agge::align_near, dummy_get, dummy_compare, true, }, @@ -374,7 +375,7 @@ namespace micro_profiler { // INIT vector invalidations; - headers_model::column columns[] = { + column_t columns[] = { { "id1", T("first"), }, { "id2", T("second"), 0, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T("third"), 0, agge::align_near, dummy_get, dummy_compare, true, }, @@ -404,13 +405,13 @@ namespace micro_profiler test( UnchangedModelIsStoredAsProvidedAtConstruction ) { // INIT - headers_model::column columns1[] = { + column_t columns1[] = { { "id1", T("Index"), 1, }, { "id2", T("Function"), 2, agge::align_near, dummy_get, dummy_compare, false, }, { "id3", T("Exclusive Time"), 3, agge::align_near, dummy_get, dummy_compare, true, }, { "fourth", T("Inclusive Time"), 4, agge::align_near, dummy_get, dummy_compare, true, }, }; - headers_model::column columns2[] = { + column_t columns2[] = { { "id1", T("a first column"), 191, }, { "id2", T("a second column"), 171, agge::align_near, dummy_get, dummy_compare, true, }, }; @@ -489,11 +490,11 @@ namespace micro_profiler test( ModelStateIsUpdatedOnLoad ) { // INIT - headers_model::column columns1[] = { + column_t columns1[] = { { "id1", T(""), 1, }, { "id2", T(""), 2, agge::align_near, dummy_get, dummy_compare, true, }, }; - headers_model::column columns2[] = { + column_t columns2[] = { { "col1", T(""), }, { "col2", T(""), 0, agge::align_near, dummy_get, dummy_compare, true, }, { "col3", T(""), 0, agge::align_near, dummy_get, dummy_compare, true, }, @@ -544,7 +545,7 @@ namespace micro_profiler test( MissingColumnsAreNotUpdatedAndInvalidOrderColumnIsFixedOnModelStateLoad ) { // INIT - headers_model::column columns[] = { + column_t columns[] = { { "col1", T(""), 13, }, { "col2", T(""), 17, agge::align_near, dummy_get, dummy_compare, true, }, { "col3", T(""), 31, agge::align_near, dummy_get, dummy_compare, true, }, diff --git a/frontend/tests/ProcessListTests.cpp b/frontend/tests/ProcessListTests.cpp index 2cfd576c2..89ab266a7 100644 --- a/frontend/tests/ProcessListTests.cpp +++ b/frontend/tests/ProcessListTests.cpp @@ -2,6 +2,7 @@ #include "helpers.h" +#include #include #include @@ -11,46 +12,27 @@ namespace micro_profiler { namespace tests { - namespace mocks - { - class process : public micro_profiler::process - { - public: - process(unsigned pid, string name) - : _pid(pid), _name(name) - { } - - virtual unsigned get_pid() const - { return _pid; } - - virtual std::string name() const - { return _name; } - - virtual void remote_execute(injection_function_t * /*injection_function*/, const_byte_range /*payload*/) - { } + begin_test_suite( ProcessListTests ) - private: - unsigned _pid; - string _name; - }; - } + shared_ptr processes; - begin_test_suite( ProcessListTests ) + static process_info make_process(id_t pid, string name, id_t parent_pid = 0) + { + process_info result = { pid, parent_pid, name, }; + return result; + } - template - static process_list::process_enumerator_t enumerate_processes(shared_ptr (&processes)[n]) + init( CreateModel ) { - return [&] (const process::enumerate_callback_t &callback) { - for (size_t i = 0; i != n; ++i) - callback(processes[i]); - }; + processes = make_shared(); } + test( ProcessListIsEmptyInitially ) { // INIT / ACT - process_list l; - wpl::richtext_table_model &t = l; + const auto l = process_list(processes, c_processes_columns); + wpl::richtext_table_model &t = *l; // ACT / ASSERT assert_equal(0u, t.get_count()); @@ -60,52 +42,51 @@ namespace micro_profiler test( ProcessListEmptyAfterUpdateWithNoProcesses ) { // INIT - process_list l; + const auto l = process_list(processes, c_processes_columns); // ACT - l.update([] (const process::enumerate_callback_t &/*callback*/) {}); + processes->invalidate(); // ASSERT - assert_equal(0u, l.get_count()); + assert_equal(0u, l->get_count()); } test( ProcessListIsPopulatedFromEnumerator ) { // INIT - process_list l; - shared_ptr p1[] = { - shared_ptr(new mocks::process(12, "foo")), - shared_ptr(new mocks::process(12111, "bar")), - }; - shared_ptr p2[] = { - shared_ptr(new mocks::process(1, "FOO")), - shared_ptr(new mocks::process(11111, "bar")), - shared_ptr(new mocks::process(16111, "BAZ")), - }; + const auto l = process_list(processes, c_processes_columns); string text; // ACT - l.update(enumerate_processes(p1)); + add_records(*processes, plural + + make_process(12, "foo") + + make_process(12111, "bar")); + processes->invalidate(); // ASSERT - assert_equal(2u, l.get_count()); - assert_equal("foo", get_text(l, 0, 0)); - assert_equal("12", get_text(l, 0, 1)); - assert_equal("bar", get_text(l, 1, 0)); - assert_equal("12111", get_text(l, 1, 1)); + assert_equal(2u, l->get_count()); + assert_equal("foo", get_text(*l, 0, 0)); + assert_equal("12", get_text(*l, 0, 1)); + assert_equal("bar", get_text(*l, 1, 0)); + assert_equal("12111", get_text(*l, 1, 1)); // ACT - l.update(enumerate_processes(p2)); + processes->clear(); + add_records(*processes, plural + + make_process(1, "FOO") + + make_process(11111, "bar") + + make_process(16111, "BAZ")); + processes->invalidate(); // ASSERT - assert_equal(3u, l.get_count()); - assert_equal("FOO", get_text(l, 0, 0)); - assert_equal("1", get_text(l, 0, 1)); - assert_equal("bar", get_text(l, 1, 0)); - assert_equal("11111", get_text(l, 1, 1)); - assert_equal("BAZ", get_text(l, 2, 0)); - assert_equal("16111", get_text(l, 2, 1)); + assert_equal(3u, l->get_count()); + assert_equal("FOO", get_text(*l, 0, 0)); + assert_equal("1", get_text(*l, 0, 1)); + assert_equal("bar", get_text(*l, 1, 0)); + assert_equal("11111", get_text(*l, 1, 1)); + assert_equal("BAZ", get_text(*l, 2, 0)); + assert_equal("16111", get_text(*l, 2, 1)); } @@ -113,137 +94,105 @@ namespace micro_profiler { // INIT size_t n_count = 0; - process_list l; - shared_ptr p[] = { - shared_ptr(new mocks::process(12, "foo")), - shared_ptr(new mocks::process(12111, "bar")), - }; - wpl::slot_connection c = l.invalidate += [&] (wpl::table_model_base::index_type item) { - n_count = l.get_count(); + const auto l = process_list(processes, c_processes_columns); + wpl::slot_connection c = l->invalidate += [&] (wpl::table_model_base::index_type item) { + n_count = l->get_count(); assert_equal(wpl::table_model_base::npos(), item); }; // ACT - l.update(enumerate_processes(p)); + processes->invalidate(); // ASSERT - assert_equal(2u, n_count); + assert_equal(0u, n_count); // ACT - l.update([&] (const process::enumerate_callback_t &/*callback*/) { }); + add_records(*processes, plural + + make_process(12, "Lorem") + + make_process(12111, "Amet") + + make_process(1211, "Quand")); + processes->invalidate(); // ASSERT - assert_equal(0u, n_count); - } - - - test( ProcessIsReturnedByIndex ) - { - // INIT - process_list l; - shared_ptr p[] = { - shared_ptr(new mocks::process(12, "foo")), - shared_ptr(new mocks::process(12111, "bar")), - shared_ptr(new mocks::process(12113, "BAZ")), - }; - - l.update(enumerate_processes(p)); - - // ACT / ASSERT - assert_equal(p[0], l.get_process(0)); - assert_equal(p[1], l.get_process(1)); - assert_equal(p[2], l.get_process(2)); + assert_equal(3u, n_count); } test( ProcessListIsSortable ) { // INIT - process_list l; - shared_ptr p[] = { - shared_ptr(new mocks::process(12, "Lorem")), - shared_ptr(new mocks::process(12111, "Amet")), - shared_ptr(new mocks::process(1211, "Quand")), - }; - string text; + const auto l = process_list(processes, c_processes_columns); - l.update(enumerate_processes(p)); + add_records(*processes, plural + + make_process(12, "Lorem") + + make_process(12111, "Amet") + + make_process(1211, "Quand")); + processes->invalidate(); // ACT - l.set_order(1, true); + l->set_order(1, true); // ASSERT - assert_equal("Lorem", get_text(l, 0, 0)); - assert_equal(p[0], l.get_process(0)); - assert_equal("Quand", get_text(l, 1, 0)); - assert_equal(p[2], l.get_process(1)); - assert_equal("Amet", get_text(l, 2, 0)); - assert_equal(p[1], l.get_process(2)); + assert_equal("Lorem", get_text(*l, 0, 0)); + assert_equal("Quand", get_text(*l, 1, 0)); + assert_equal("Amet", get_text(*l, 2, 0)); // ACT - l.set_order(1, false); + l->set_order(1, false); // ASSERT - assert_equal("Amet", get_text(l, 0, 0)); - assert_equal("Quand", get_text(l, 1, 0)); - assert_equal("Lorem", get_text(l, 2, 0)); + assert_equal("Amet", get_text(*l, 0, 0)); + assert_equal("Quand", get_text(*l, 1, 0)); + assert_equal("Lorem", get_text(*l, 2, 0)); // ACT - l.set_order(0, true); + l->set_order(0, true); // ASSERT - assert_equal("12111", get_text(l, 0, 1)); - assert_equal("12", get_text(l, 1, 1)); - assert_equal("1211", get_text(l, 2, 1)); + assert_equal("12111", get_text(*l, 0, 1)); + assert_equal("12", get_text(*l, 1, 1)); + assert_equal("1211", get_text(*l, 2, 1)); // ACT - l.set_order(0, false); + l->set_order(0, false); // ASSERT - assert_equal("1211", get_text(l, 0, 1)); - assert_equal(p[2], l.get_process(0)); - assert_equal("12", get_text(l, 1, 1)); - assert_equal(p[0], l.get_process(1)); - assert_equal("12111", get_text(l, 2, 1)); - assert_equal(p[1], l.get_process(2)); + assert_equal("1211", get_text(*l, 0, 1)); + assert_equal("12", get_text(*l, 1, 1)); + assert_equal("12111", get_text(*l, 2, 1)); } test( SortOrderIsAppliedOnUpdate ) { // INIT - process_list l; - shared_ptr p1[] = { - shared_ptr(new mocks::process(12, "Lorem")), - shared_ptr(new mocks::process(12111, "Amet")), - shared_ptr(new mocks::process(1211, "Quand")), - }; - shared_ptr p2[] = { - shared_ptr(new mocks::process(12, "Lorem")), - shared_ptr(new mocks::process(12111, "Amet")), - shared_ptr(new mocks::process(1311, "Dolor")), - shared_ptr(new mocks::process(1211, "Quand")), - }; + const auto l = process_list(processes, c_processes_columns); string text; - l.set_order(0, true); + l->set_order(0, true); // ACT - l.update(enumerate_processes(p1)); + add_records(*processes, plural + + make_process(12, "Lorem") + + make_process(12111, "Amet") + + make_process(1211, "Quand")); + processes->invalidate(); // ASSERT - assert_equal("12111", get_text(l, 0, 1)); - assert_equal("12", get_text(l, 1, 1)); - assert_equal("1211", get_text(l, 2, 1)); + assert_equal("12111", get_text(*l, 0, 1)); + assert_equal("12", get_text(*l, 1, 1)); + assert_equal("1211", get_text(*l, 2, 1)); // ACT - l.update(enumerate_processes(p2)); + add_records(*processes, plural + + make_process(1311, "Dolor")); + processes->invalidate(); // ASSERT - assert_equal("12111", get_text(l, 0, 1)); - assert_equal("1311", get_text(l, 1, 1)); - assert_equal("12", get_text(l, 2, 1)); - assert_equal("1211", get_text(l, 3, 1)); + assert_equal("12111", get_text(*l, 0, 1)); + assert_equal("1311", get_text(*l, 1, 1)); + assert_equal("12", get_text(*l, 2, 1)); + assert_equal("1211", get_text(*l, 3, 1)); } end_test_suite } diff --git a/injector/process.h b/injector/process.h index 3977b47be..55ac714ce 100644 --- a/injector/process.h +++ b/injector/process.h @@ -21,7 +21,6 @@ #pragma once #include -#include #include #include @@ -30,13 +29,11 @@ namespace micro_profiler struct process { typedef void (injection_function_t)(const_byte_range payload); - typedef std::function &entry)> enumerate_callback_t; virtual unsigned get_pid() const = 0; virtual std::string name() const = 0; virtual void remote_execute(injection_function_t *injection_function, const_byte_range payload) = 0; static std::shared_ptr open(unsigned int pid); - static void enumerate(const enumerate_callback_t &callback); }; } diff --git a/injector/src/process_win32.cpp b/injector/src/process_win32.cpp index dfd54a6f3..63323f1a1 100644 --- a/injector/src/process_win32.cpp +++ b/injector/src/process_win32.cpp @@ -27,7 +27,6 @@ #include #include #include -#include using namespace std; using namespace std::placeholders; @@ -144,19 +143,4 @@ namespace micro_profiler shared_ptr process::open(unsigned int pid) { return shared_ptr(new win32::process(pid, string())); } - - void process::enumerate(const enumerate_callback_t &callback) - { - shared_ptr snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), &::CloseHandle); - PROCESSENTRY32 entry = { sizeof(PROCESSENTRY32), }; - - for (auto lister = &::Process32First; lister(snapshot.get(), &entry); lister = &::Process32Next) - try - { - callback(shared_ptr(new win32::process(entry.th32ProcessID, unicode(entry.szExeFile)))); - } - catch (...) - { - } - } } diff --git a/injector/tests/ProcessDiscoverTests.cpp b/injector/tests/ProcessDiscoverTests.cpp index 4a608eddf..503832761 100644 --- a/injector/tests/ProcessDiscoverTests.cpp +++ b/injector/tests/ProcessDiscoverTests.cpp @@ -87,50 +87,6 @@ namespace micro_profiler // ACT / ASSERT controller->wait_connection(); } - - - test( RunningProcessesAreListedOnEnumeration ) - { - // INIT - multimap< string, shared_ptr > names; - process::enumerate_callback_t cb = [&] (shared_ptr p) { - string name = p->name(); - - name = name.substr(0, name.rfind('.')); - names.insert(make_pair(name, p)); - }; - shared_ptr child1 = create_process("./guinea_runner", " \"" + controller_id + "\""); - controller->wait_connection(); - shared_ptr child2 = create_process("./guinea_runner", " \"" + controller_id + "\""); - controller->wait_connection(); - shared_ptr child3 = create_process("./guinea_runner2", " \"" + controller_id + "\""); - controller->wait_connection(); - shared_ptr child4 = create_process("./guinea_runner3", " \"" + controller_id + "\""); - controller->wait_connection(); - - // ACT - process::enumerate(cb); - - // ASSERT - assert_equal(2u, names.count("guinea_runner")); - assert_equal(1u, names.count("guinea_runner2")); - assert_equal(child3->get_pid(), names.find("guinea_runner2")->second->get_pid()); - assert_equal(1u, names.count("guinea_runner3")); - assert_equal(child3->get_pid(), names.find("guinea_runner2")->second->get_pid()); - - // INIT - names.clear(); - controller->sessions[2]->disconnect_client(); - child3->wait(); - - // ACT - process::enumerate(cb); - - // ASSERT - assert_equal(2u, names.count("guinea_runner")); - assert_equal(0u, names.count("guinea_runner2")); - assert_equal(1u, names.count("guinea_runner3")); - } end_test_suite } } diff --git a/micro-profiler/frontend/attach_ui.cpp b/micro-profiler/frontend/attach_ui.cpp index ddc3bde0a..86d55d420 100644 --- a/micro-profiler/frontend/attach_ui.cpp +++ b/micro-profiler/frontend/attach_ui.cpp @@ -21,8 +21,10 @@ #include "attach_ui.h" #include "inject_profiler.h" +#include #include #include +#include #include #include #include @@ -37,57 +39,51 @@ namespace micro_profiler { const auto secondary = agge::style::height(10); - const headers_model::column c_columns_processes[] = { - { "ProcessExe", "Process\n" + secondary + "executable", 384, agge::align_near, }, - { "ProcessID", "PID" + secondary, 384, agge::align_far, }, + struct pid + { + id_t operator ()(const process_info &record) const { return record.pid; } }; } - attach_ui::attach_ui(const factory &factory_, const wpl::queue &queue_, const string &frontend_id) - : wpl::stack(false, factory_.context.cursor_manager_), _processes_lv(factory_.create_control("listview")), - _model(new process_list), _queue(queue_), _alive(make_shared(true)) + attach_ui::attach_ui(const factory &factory_, shared_ptr processes, const string &frontend_id) + : wpl::stack(false, factory_.context.cursor_manager_) { shared_ptr toolbar; shared_ptr