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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325 | --
-- Copyright (c) 2020-2023, Adel Noureddine, Université de Pau et des Pays de l'Adour.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the
-- GNU General Public License v3.0 only (GPL-3.0-only)
-- which accompanies this distribution, and is available at:
-- https://www.gnu.org/licenses/gpl-3.0.en.html
--
-- Author : Adel Noureddine
--
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with GNAT.Command_Line; use GNAT.Command_Line;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with GNAT.OS_Lib; use GNAT.OS_Lib;
with GNAT.Ctrl_C; use GNAT.Ctrl_C;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
with Ada.Command_Line; use Ada.Command_Line;
with CPU_Cycles; use CPU_Cycles;
with CSV_Power; use CSV_Power;
with Help_Info; use Help_Info;
with CPU_STAT_PID; use CPU_STAT_PID;
with Intel_RAPL_sysfs; use Intel_RAPL_sysfs;
with OS_Utils; use OS_Utils;
with Nvidia_SMI; use Nvidia_SMI;
with Raspberry_Pi_CPU_Formula; use Raspberry_Pi_CPU_Formula;
with CPU_STAT_App; use CPU_STAT_App;
procedure Powerjoular is
-- Power variables
--
-- CPU Power
CPU_Power : Float; -- Entire CPU power consumption
Previous_CPU_Power : Float := 0.0; -- Previous CPU power consumption (t - 1)
PID_CPU_Power : Float; -- CPU power consumption of monitored PID
App_CPU_Power : Float; -- CPU power consumption of monitored application
CPU_Energy : Float := 0.0;
--
-- GPU Power
GPU_Power : Float := 0.0;
Previous_GPU_Power : Float := 0.0; -- Previous GPU power consumption (t - 1)
GPU_Energy : Float := 0.0;
--
-- Total Power and Energy
Previous_Total_Power : Float := 0.0; -- Previous entire total power consumption (t - 1)
Total_Power : Float := 0.0; -- Total power consumption of all hardware components
Total_Energy : Float := 0.0; -- Total energy consumed since start of PowerJoular until exit
-- Data types for Intel RAPL energy monitoring
RAPL_Before : Intel_RAPL_Data; -- Intel RAPL data
RAPL_After : Intel_RAPL_Data; -- Intel RAPL data
RAPL_Energy : Float; -- Intel RAPL energy difference for monitoring cycle
-- Data types for Nvidia energy monitoring
Nvidia_Supported : Boolean; -- If nvidia card, drivers and smi tool are available
-- Raspberrry Pi model settings
Algorithm_Name : Unbounded_String := To_Unbounded_String ("polynomial"); -- Regression model type (by default, polynomial regression model)
-- Data types to monitor CPU cycles
CPU_CCI_Before : CPU_Cycles_Data; -- Entire CPU cycles
CPU_CCI_After : CPU_Cycles_Data; -- Entire CPU cycles
CPU_PID_Monitor : CPU_STAT_PID_Data; -- Monitored PID CPU cycles and power
CPU_App_Monitor : CPU_STAT_App_Data; -- Monitored App CPU cycles and power
-- CPU utilization variables
CPU_Utilization : Float; -- Entire CPU utilization
PID_CPU_Utilization : Float; -- CPU utilization of monitored PID
App_CPU_Utilization : Float; -- CPU utilization of monitored application
-- OS name
OS_Name : String := Get_OS_Name;
-- Platform name
Platform_Name : String := Get_Platform_Name;
-- CSV filenames
CSV_Filename : Unbounded_String; -- CSV filename for entire CPU power data
PID_Or_App_CSV_Filename : Unbounded_String; -- CSV filename for monitored PID or application CPU power data
-- Settings
Show_Terminal : Boolean := False; -- Show power data on terminal
Show_Debug : Boolean := False; -- Show debug info on terminal
Print_File: Boolean := False; -- Save power data in file
Monitor_PID : Boolean := False; -- Monitor a specific PID
Monitor_App : Boolean := False; -- Monitor a specific application by its name
Overwrite_Data : Boolean := false; -- Overwrite data instead of append on file
-- Procedure to capture Ctrl+C to show total energy on exit
procedure CtrlCHandler is
begin
New_Line;
Put_Line ("--------------------------");
Put ("Total energy: ");
Put (Total_Energy, Exp => 0, Fore => 0, Aft => 2);
Put_Line (" Joules, including:");
Put (HT & "CPU energy: ");
Put (CPU_Energy, Exp => 0, Fore => 0, Aft => 2);
Put_Line (" Joules");
Put (HT & "GPU energy: ");
Put (GPU_Energy, Exp => 0, Fore => 0, Aft => 2);
Put_Line (" Joules");
Put_Line ("--------------------------");
OS_Exit (0);
end CtrlCHandler;
begin
-- Capture Ctrl+C and redirect to handler
Install_Handler(Handler => CtrlCHandler'Unrestricted_Access);
-- Default CSV filename
CSV_Filename := To_Unbounded_String ("./powerjoular-power.csv");
-- Loop over command line options
loop
case Getopt ("h v t d f: p: a: o: u l") is
when 'h' => -- Show help
Show_Help;
return;
when 'v' => -- Show help
Show_Version;
return;
when 't' => -- Show power data on terminal
Show_Terminal := True;
when 'd' => -- Show debug info on terminal
Show_Debug := True;
when 'p' => -- Monitor a particular PID
-- PID_Number := Integer'Value (Parameter);
CPU_PID_Monitor.PID_Number := Integer'Value (Parameter);
Monitor_PID := True;
when 'a' => -- Monitor a particular application by its name
CPU_App_Monitor.App_Name := To_Unbounded_String (Parameter);
Monitor_App := True;
when 'f' => -- Specifiy a filename for CSV file (append data)
CSV_Filename := To_Unbounded_String (Parameter);
Print_File := True;
when 'o' => -- Specifiy a filename for CSV file (overwrite data)
CSV_Filename := To_Unbounded_String (Parameter);
Print_File := True;
Overwrite_Data := True;
when 'l' => -- Use linear regression model instead of polynomial models
Algorithm_Name := To_Unbounded_String ("linear");
when others =>
exit;
end case;
end loop;
if (Argument_Count = 0) then
Show_Terminal := True;
end if;
-- If platform not supported, then exit program
if (Platform_Name = "") then
Put_Line ("Platform not supported");
Put_Line (OS_Name);
return;
end if;
if Show_Debug then
Put_Line ("System info:");
Put_Line (Ada.Characters.Latin_1.HT & "Platform: " & Platform_Name);
end if;
if Check_Intel_Supported_System (Platform_Name) then
-- For Intel RAPL, check and populate supported packages first
Check_Supported_Packages (RAPL_Before, "psys");
if RAPL_Before.psys_supported and Show_Debug then
Put_Line (Ada.Characters.Latin_1.HT & "Intel RAPL psys: " & Boolean'Image (RAPL_Before.Psys_Supported));
end if;
if (not RAPL_Before.psys_supported) then -- Only check for pkg and dram if psys is not supported
Check_Supported_Packages (RAPL_Before, "pkg");
Check_Supported_Packages (RAPL_Before, "dram");
if RAPL_Before.Pkg_Supported and Show_Debug then
Put_Line (Ada.Characters.Latin_1.HT & "Intel RAPL pkg: " & Boolean'Image (RAPL_Before.pkg_supported));
end if;
if RAPL_Before.Dram_Supported and Show_Debug then
Put_Line (Ada.Characters.Latin_1.HT & "Intel RAPL dram: " & Boolean'Image (RAPL_Before.Dram_Supported));
end if;
end if;
RAPL_After := RAPL_Before; -- Populate the "after" data type with same checking as the "before" (insteaf of wasting redundant calls to procedure)
-- Check if Nvidia card is supported
-- For now, Nvidia support requiers a PC/server, thus Intel support
Nvidia_Supported := Check_Nvidia_Supported_System;
if Nvidia_Supported and Show_Debug then
Put_Line (Ada.Characters.Latin_1.HT & "Nvidia supported: " & Boolean'Image (Nvidia_Supported));
end if;
end if;
-- Amend PID CSV file with PID number
if Monitor_PID then
PID_Or_App_CSV_Filename := CSV_Filename & "-" & Trim(Integer'Image (CPU_PID_Monitor.PID_Number), Ada.Strings.Left) & ".csv";
if Show_Debug then
Put_Line ("Monitoring PID: " & Integer'Image (CPU_PID_Monitor.PID_Number));
end if;
end if;
-- Amend App CSV file with App name
if Monitor_App then
PID_Or_App_CSV_Filename := CSV_Filename & "-" & CPU_App_Monitor.App_Name & ".csv";
if Show_Debug then
Put_Line ("Monitoring application: " & To_String (CPU_App_Monitor.App_Name));
end if;
end if;
-- Main monitoring loop
loop
-- Get a first snapshot of current entire CPU cycles
Calculate_CPU_Cycles (CPU_CCI_Before);
if Monitor_PID then -- Do the same for CPU cycles of the monitored PID
Calculate_PID_Time (CPU_PID_Monitor, True);
end if;
if Monitor_App then -- Do the same for CPU cycles of the monitored application
-- First update the PID array for the application
-- We do it every cycle so PID list is always current and accurate
Update_PID_Array (CPU_App_Monitor);
Calculate_App_Time (CPU_App_Monitor, True);
end if;
if Check_Intel_Supported_System (Platform_Name) then
-- Get a first snapshot of Intel RAPL energy data
Calculate_Energy (RAPL_Before);
end if;
-- Wait for 1 second
delay 1.0;
-- Get a second snapshot of current entire CPU cycles
Calculate_CPU_Cycles (CPU_CCI_After);
if Monitor_PID then -- Do the same for CPU cycles of the monitored PID
Calculate_PID_Time (CPU_PID_Monitor, False);
end if;
if Monitor_App then -- Do the same for CPU cycles of the monitored application
Calculate_App_Time (CPU_App_Monitor, False);
end if;
if Check_Intel_Supported_System (Platform_Name) then
-- Get a first snapshot of Intel RAPL energy data
Calculate_Energy (RAPL_After);
end if;
-- Calculate entire CPU utilization
CPU_Utilization := (Float (CPU_CCI_After.cbusy) - Float (CPU_CCI_Before.cbusy)) / (Float (CPU_CCI_After.ctotal) - Float (CPU_CCI_Before.ctotal));
if Check_Raspberry_Pi_Supported_System (Platform_Name) then
-- Calculate power consumption for Raspberry
CPU_Power := Calculate_CPU_Power (CPU_Utilization, Platform_Name, To_String (Algorithm_Name));
Total_Power := CPU_Power;
end if;
if Check_Intel_Supported_System (Platform_Name) then
-- Calculate Intel RAPL energy consumption
RAPL_Energy := RAPL_After.total_energy - RAPL_Before.total_energy;
CPU_Power := RAPL_Energy;
Total_Power := CPU_Power;
end if;
if Nvidia_Supported then
-- Calculate GPU power consumption
GPU_Power := Get_Nvidia_SMI_Power;
-- Add GPU power to total power
-- The total power displayed by PowerJoular is therefore : CPU + GPU power
Total_Power := Total_Power + GPU_Power;
end if;
-- If a particular PID is monitored, calculate its CPU time, CPU utilization and CPU power
if Monitor_PID then
PID_CPU_Utilization := (Float (CPU_PID_Monitor.Monitored_Time)) / (Float (CPU_CCI_After.ctotal) - Float (CPU_CCI_Before.ctotal));
PID_CPU_Power := (PID_CPU_Utilization * CPU_Power) / CPU_Utilization;
-- Show CPU power data on terminal of monitored PID
if Show_Terminal then
Show_On_Terminal_PID (PID_CPU_Utilization, PID_CPU_Power, CPU_Utilization, CPU_Power, True);
end if;
-- Save CPU power data to CSV file of monitored PID
if Print_File then
Save_PID_To_CSV_File (To_String (PID_Or_App_CSV_Filename), PID_CPU_Utilization, PID_CPU_Power, Overwrite_Data);
end if;
end if;
-- If a particular application is monitored, calculate its CPU time, CPU utilization and CPU power
if Monitor_App then
-- PID_Time := CPU_PID_After.total_time - CPU_PID_Before.total_time;
App_CPU_Utilization := (Float (CPU_App_Monitor.Monitored_Time)) / (Float (CPU_CCI_After.ctotal) - Float (CPU_CCI_Before.ctotal));
App_CPU_Power := (App_CPU_Utilization * CPU_Power) / CPU_Utilization;
-- Show CPU power data on terminal of monitored PID
if Show_Terminal then
Show_On_Terminal_PID (App_CPU_Utilization, App_CPU_Power, CPU_Utilization, CPU_Power, False);
end if;
-- Save CPU power data to CSV file of monitored PID
if Print_File then
Save_PID_To_CSV_File (To_String (PID_Or_App_CSV_Filename), App_CPU_Utilization, App_CPU_Power, Overwrite_Data);
end if;
end if;
-- Show total power data on terminal
if Show_Terminal and then (not Monitor_PID) and then (not Monitor_App) then
Show_On_Terminal (CPU_Utilization, Total_Power, Previous_Total_Power, CPU_Power, GPU_Power, Nvidia_Supported);
end if;
Previous_CPU_Power := CPU_Power;
Previous_GPU_Power := GPU_Power;
Previous_Total_Power := Total_Power;
-- Increment total energy with power of current cycle
-- Cycle is 1 second, so energy for 1 sec = power
Total_Energy := Total_Energy + Total_Power;
CPU_Energy := CPU_Energy + CPU_Power;
GPU_Energy := GPU_Energy + GPU_Power;
-- Save total power data to CSV file
if Print_File then
Save_To_CSV_File (To_String (CSV_Filename), CPU_Utilization, Total_Power, CPU_Power, GPU_Power, Overwrite_Data);
end if;
end loop;
end Powerjoular;
|