Repository navigation
Expand file tree
/
Copy pathexample_cpu_load.adb
More file actions
executable file
·185 lines (149 loc) · 6.37 KB
/
Copy pathexample_cpu_load.adb
File metadata and controls
executable file
·185 lines (149 loc) · 6.37 KB
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
--
-- Copyright (c) 2026, Adel Noureddine.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the
-- GNU Lesser General Public License v3.0 only (LGPL-3.0-only)
-- which accompanies this distribution, and is available at:
-- https://www.gnu.org/licenses/lgpl-3.0.en.html
--
-- Author : Adel Noureddine
--
-- Prints the CPU load of the machine, of this very program, and of an application named on the command line, every second, until stopped with Ctrl+C
--
-- Build and run it with (the system is detected on its own, -XPJ_OS overrides it: linux, macos, windows or freebsd):
-- gprbuild -P example/example.gpr -p
-- ./example/example_cpu_load firefox
--
-- A number after the name stops the program after that many readings ("" follows no application):
-- ./example/example_cpu_load "" 3
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Strings; use Ada.Strings;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Ctrl_C;
with GNAT.OS_Lib;
with CPU_Load; use CPU_Load;
-- use type: the Total = 0 check below compares Integer_64
with Interfaces;
use type Interfaces.Integer_64;
procedure Example_CPU_Load is
Interval : constant Duration := 1.0;
-- Atomic, as the handler runs in another thread on Windows
Stop_Asked : Boolean := False with Atomic;
-- Only asks the loop to stop: printing is not safe from a handler
procedure On_Ctrl_C is
begin
Stop_Asked := True;
end On_Ctrl_C;
package Value_IO is new Ada.Text_IO.Float_IO (Long_Float);
-- ANSI colours: cyan machine, magenta this program, yellow application, green ready, red trouble
Escape : constant Character := ASCII.ESC;
Reset : constant String := Escape & "[0m";
Machine_Colour : constant String := Escape & "[1;36m";
Mine_Colour : constant String := Escape & "[1;35m";
App_Colour : constant String := Escape & "[1;33m";
Ready_Colour : constant String := Escape & "[1;32m";
Trouble_Colour : constant String := Escape & "[1;31m";
-- Back to the start of the line and erase it, so each reading overwrites the last
Clear_Line : constant String := ASCII.CR & Escape & "[2K";
-- A load is a share of the whole machine: one core fully busy out of eight reads 12.5%
function Image (Colour : in String;
Name : in String;
Load : in Long_Float) return String is
Machine_Share : String (1 .. 12);
Unreadable : constant String := Colour & Name & " n/a" & Reset;
begin
-- Negative: the load could not be read (process gone, or not allowed)
if Load < 0.0 then
return Unreadable;
end if;
Value_IO.Put (To => Machine_Share, Item => 100.0 * Load, Aft => 2, Exp => 0);
return Colour & Name
& " " & Trim (Machine_Share, Left) & "%"
& Reset;
exception
-- The value does not fit in the buffer
when others =>
return Unreadable;
end Image;
Ours : constant Process_ID :=
GNAT.OS_Lib.Pid_To_Integer (GNAT.OS_Lib.Current_Process_Id);
-- No name given means no application is followed
App : constant String :=
(if Argument_Count >= 1 then Argument (1) else "");
-- How many readings to take before stopping, or zero to run until Ctrl+C
Wanted_Readings : Natural := 0;
Taken : Natural := 0;
-- Read the machine once per loop so all three loads cover the same interval
Machine_Before, Machine_After : Sample;
Mine_Before, Mine_After : Sample;
App_Before, App_After : Sample;
begin
if Argument_Count >= 2 then
begin
Wanted_Readings := Natural'Value (Argument (2));
exception
when Constraint_Error =>
Put_Line (Standard_Error, "usage: " & Command_Name & " [application] [number of readings]");
Set_Exit_Status (Failure);
return;
end;
end if;
Put_Line (Ready_Colour & "CPU Load" & Reset);
if App = "" then
Put_Line ("Following the machine and this program."
& " Name an application to follow it as well:"
& " " & Command_Name & " firefox");
else
Put_Line ("Following the machine, this program, and " & App);
end if;
-- Unrestricted_Access: the handler is nested in this procedure
GNAT.Ctrl_C.Install_Handler (On_Ctrl_C'Unrestricted_Access);
Machine_Before := Take;
Mine_Before := Take (Ours, Machine_Before);
App_Before := Take (App, Machine_Before);
-- Total 0: the counters could not be read; without this it would print 0% forever and look idle
if Machine_Before.Total = 0 then
Put_Line (Trouble_Colour
& "The machine's counters could not be read at all."
& Reset);
#if PJ_MACOS then
Put_Line ("Was this built for another system? Rebuild with -XPJ_OS=macos");
#elsif PJ_WINDOWS then
Put_Line ("Was this built for another system? Rebuild with -XPJ_OS=windows");
#elsif PJ_FREEBSD then
Put_Line ("Was this built for another system? Rebuild with -XPJ_OS=freebsd");
#else
Put_Line ("Is /proc mounted? Otherwise rebuild with -XPJ_OS=linux");
#end if;
Set_Exit_Status (Failure);
return;
end if;
while not Stop_Asked loop
delay Interval;
-- Ctrl+C interrupts the delay above, so don't print one last reading after it
exit when Stop_Asked;
Machine_After := Take;
Mine_After := Take (Ours, Machine_After);
App_After := Take (App, Machine_After);
Put (Clear_Line
& Image (Machine_Colour, "machine",
System_Usage (Machine_Before, Machine_After))
& " | "
& Image (Mine_Colour, "this program",
Process_Usage (Mine_Before, Mine_After)));
if App /= "" then
Put (" | " & Image (App_Colour, App,
Process_Usage (App_Before, App_After)));
end if;
Flush;
Machine_Before := Machine_After;
Mine_Before := Mine_After;
App_Before := App_After;
Taken := Taken + 1;
exit when Wanted_Readings > 0 and then Taken >= Wanted_Readings;
end loop;
-- The readings share one line; end it first
New_Line;
Put_Line (Ready_Colour & "Stopping" & Reset);
end Example_CPU_Load;