summaryrefslogtreecommitdiff
path: root/Userland/Applications/SystemMonitor/ProcessModel.h
blob: 9822ffbcccf2b44ea99fdf22a0a2d4882e49dc06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*
 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
 * Copyright (c) 2022, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/DeprecatedString.h>
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtrVector.h>
#include <AK/Vector.h>
#include <LibGUI/Icon.h>
#include <LibGUI/Model.h>
#include <LibGUI/ModelIndex.h>
#include <sys/types.h>
#include <unistd.h>

class GraphWidget;

class ProcessModel final : public GUI::Model {
public:
    enum Column {
        Icon = 0,
        Name,
        PID,
        TID,
        CPU,
        State,
        User,
        Virtual,
        DirtyPrivate,
        Pledge,
        Physical,
        CleanInode,
        PurgeableVolatile,
        PurgeableNonvolatile,
        Veil,
        Processor,
        Priority,
        PPID,
        PGID,
        SID,
        Syscalls,
        InodeFaults,
        ZeroFaults,
        CowFaults,
        FileReadBytes,
        FileWriteBytes,
        UnixSocketReadBytes,
        UnixSocketWriteBytes,
        IPv4SocketReadBytes,
        IPv4SocketWriteBytes,
        Command,
        __Count
    };

    static ProcessModel& the();

    static NonnullRefPtr<ProcessModel> create() { return adopt_ref(*new ProcessModel); }
    virtual ~ProcessModel() override = default;

    virtual int tree_column() const override { return Column::Name; }
    virtual int row_count(GUI::ModelIndex const&) const override;
    virtual int column_count(GUI::ModelIndex const&) const override;
    virtual DeprecatedString column_name(int column) const override;
    virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override;
    virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const& parent = {}) const override;
    virtual GUI::ModelIndex parent_index(GUI::ModelIndex const&) const override;
    virtual bool is_searchable() const override { return true; }
    virtual Vector<GUI::ModelIndex> matches(StringView, unsigned = MatchesFlag::AllMatching, GUI::ModelIndex const& = GUI::ModelIndex()) override;
    virtual bool is_column_sortable(int column_index) const override { return column_index != Column::Icon; }
    void update();
    bool is_default_column(int index) const;

    struct CpuInfo {
        u32 id;
        float total_cpu_percent { 0.0 };
        float total_cpu_percent_kernel { 0.0 };

        explicit CpuInfo(u32 id)
            : id(id)
        {
        }
    };

    Function<void(NonnullOwnPtrVector<CpuInfo> const&)> on_cpu_info_change;
    Function<void(int process_count, int thread_count)> on_state_update;

    NonnullOwnPtrVector<CpuInfo> const& cpus() const { return m_cpus; }

private:
    ProcessModel();

    struct Process;

    struct ThreadState {
        pid_t tid { 0 };
        pid_t pid { 0 };
        pid_t ppid { 0 };
        pid_t pgid { 0 };
        pid_t sid { 0 };
        u64 time_user { 0 };
        u64 time_kernel { 0 };
        bool kernel { false };
        DeprecatedString executable { "" };
        DeprecatedString name { "" };
        DeprecatedString command { "" };
        uid_t uid { 0 };
        DeprecatedString state { "" };
        DeprecatedString user { "" };
        DeprecatedString pledge { "" };
        DeprecatedString veil { "" };
        u32 cpu { 0 };
        u32 priority { 0 };
        size_t amount_virtual { 0 };
        size_t amount_resident { 0 };
        size_t amount_dirty_private { 0 };
        size_t amount_clean_inode { 0 };
        size_t amount_purgeable_volatile { 0 };
        size_t amount_purgeable_nonvolatile { 0 };
        unsigned syscall_count { 0 };
        unsigned inode_faults { 0 };
        unsigned zero_faults { 0 };
        unsigned cow_faults { 0 };
        unsigned unix_socket_read_bytes { 0 };
        unsigned unix_socket_write_bytes { 0 };
        unsigned ipv4_socket_read_bytes { 0 };
        unsigned ipv4_socket_write_bytes { 0 };
        unsigned file_read_bytes { 0 };
        unsigned file_write_bytes { 0 };
        float cpu_percent { 0 };
        float cpu_percent_kernel { 0 };
        Process& process;

        ThreadState(Process& argument_process)
            : process(argument_process)
        {
        }
        ThreadState(ThreadState&& other) = default;
        ThreadState& operator=(ThreadState&& other)
        {
            this->tid = other.tid;
            this->pid = other.pid;
            this->ppid = other.ppid;
            this->pgid = other.pgid;
            this->sid = other.sid;
            this->time_user = other.time_user;
            this->time_kernel = other.time_kernel;
            this->kernel = other.kernel;
            this->executable = other.executable;
            this->name = other.name;
            this->command = other.command;
            this->uid = other.uid;
            this->state = other.state;
            this->user = other.user;
            this->pledge = other.pledge;
            this->veil = other.veil;
            this->cpu = other.cpu;
            this->priority = other.priority;
            this->amount_virtual = other.amount_virtual;
            this->amount_resident = other.amount_resident;
            this->amount_dirty_private = other.amount_dirty_private;
            this->amount_clean_inode = other.amount_clean_inode;
            this->amount_purgeable_volatile = other.amount_purgeable_volatile;
            this->amount_purgeable_nonvolatile = other.amount_purgeable_nonvolatile;
            this->syscall_count = other.syscall_count;
            this->inode_faults = other.inode_faults;
            this->zero_faults = other.zero_faults;
            this->cow_faults = other.cow_faults;
            this->unix_socket_read_bytes = other.unix_socket_read_bytes;
            this->unix_socket_write_bytes = other.unix_socket_write_bytes;
            this->ipv4_socket_read_bytes = other.ipv4_socket_read_bytes;
            this->ipv4_socket_write_bytes = other.ipv4_socket_write_bytes;
            this->file_read_bytes = other.file_read_bytes;
            this->file_write_bytes = other.file_write_bytes;
            this->cpu_percent = other.cpu_percent;
            this->cpu_percent_kernel = other.cpu_percent_kernel;
            this->process = other.process;

            return *this;
        }
        ~ThreadState() = default;
    };

    struct Thread : public RefCounted<Thread> {
        ThreadState current_state;
        ThreadState previous_state;

        Thread(Process& process)
            : current_state(process)
            , previous_state(process)
        {
        }

        bool operator==(Thread const& other) const
        {
            return current_state.tid == other.current_state.tid;
        }

        bool is_main_thread() const
        {
            return current_state.tid == current_state.process.pid;
        }
    };

    struct Process {
        pid_t pid;
        NonnullRefPtrVector<Thread> threads;

        bool operator==(Process const& other) const
        {
            return this->pid == other.pid;
        }

        Optional<NonnullRefPtr<Thread>> main_thread() const
        {
            return threads.first_matching([this](auto const thread) { return thread->current_state.tid == pid; });
        }

        // Return anything but the main thread; therefore, valid indices are anything up to threads.size()-1 exclusive.
        Thread const& non_main_thread(size_t index) const
        {
            auto main_thread_index = -1;
            for (size_t i = 0; i < threads.size(); ++i) {
                if (threads[i].is_main_thread()) {
                    main_thread_index = static_cast<int>(i);
                    break;
                }
            }
            VERIFY(main_thread_index >= 0);
            // Shift all indices starting from the main thread's index upwards, so that the user doesn't have to worry about index discontinuities.
            if (index >= static_cast<size_t>(main_thread_index))
                return threads[index + 1];
            return threads[index];
        }
    };

    GUI::Icon icon_for(Thread const& thread) const;

    int thread_model_row(Thread const& thread) const;

    // The thread list contains the same threads as the Process structs.
    HashMap<int, NonnullRefPtr<Thread>> m_threads;
    NonnullOwnPtrVector<Process> m_processes;
    NonnullOwnPtrVector<CpuInfo> m_cpus;
    RefPtr<Core::File> m_proc_all;
    GUI::Icon m_kernel_process_icon;
    u64 m_total_time_scheduled { 0 };
    u64 m_total_time_scheduled_kernel { 0 };
    bool m_has_total_scheduled_time { false };
};