PageRenderTime 68ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/postproc-lib/lib/SP/Endurance/GraphGenerators.pm

https://github.com/mkosola/sp-endurance
Perl | 2782 lines | 2281 code | 474 blank | 27 comment | 95 complexity | f9cb0ab544d78530ee0e7a560f41532b MD5 | raw file
Possible License(s): GPL-2.0
  1. # This file is part of sp-endurance.
  2. #
  3. # vim: ts=4:sw=4:et
  4. #
  5. # Copyright (C) 2010-2012 by Nokia Corporation
  6. #
  7. # Contact: Eero Tamminen <eero.tamminen@nokia.com>
  8. #
  9. # This program is free software; you can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License
  11. # version 2 as published by the Free Software Foundation.
  12. #
  13. # This program is distributed in the hope that it will be useful, but
  14. # WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. # General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21. # 02110-1301 USA
  22. package SP::Endurance::GraphGenerators;
  23. require Exporter;
  24. @ISA = qw/Exporter/;
  25. @EXPORT_OK = qw/graph_generators get_plots/;
  26. use SP::Endurance::Parser;
  27. use SP::Endurance::Util qw/b2mb kb2mb nonzero has_changes max_change
  28. cumulative_to_changes change_per_second/;
  29. use List::Util qw/max sum/;
  30. use List::MoreUtils qw/uniq zip any/;
  31. use POSIX qw/ceil/;
  32. use Data::Dumper;
  33. no warnings 'uninitialized';
  34. eval 'use common::sense';
  35. use strict;
  36. sub CGROUP_UNLIMITED() { 9223372036854775807 }
  37. sub CLK_TCK() { 100 }
  38. sub PAGE_SIZE() { 4096 }
  39. sub SECTOR_SIZE() { 512 }
  40. sub SHM_LOCKED() { 02000 }
  41. my @plots;
  42. my @generators;
  43. sub graph_generators { @generators }
  44. sub get_plots { sort { $a->{key} cmp $b->{key} } @plots }
  45. sub register_generator {
  46. my $g = shift;
  47. return unless ref $g eq 'CODE';
  48. push @generators, $g;
  49. }
  50. our $done_plotting_cb;
  51. sub done_plotting {
  52. my $plot = shift;
  53. foreach ($plot->done_plotting) {
  54. push @plots, $_;
  55. $done_plotting_cb->($_) if ref $done_plotting_cb eq 'CODE';
  56. }
  57. }
  58. my %pid_to_cmdline;
  59. sub pid_to_cmdline {
  60. my $masterdb = shift;
  61. my $pid = shift;
  62. return unless $pid;
  63. unless (defined $pid_to_cmdline{$pid}) {
  64. my @cmdlines = uniq grep { defined && length } map {
  65. if (exists $_->{'/proc/pid/cmdline'} and exists $_->{'/proc/pid/cmdline'}->{$pid}) {
  66. $_->{'/proc/pid/cmdline'}->{$pid}
  67. } elsif (exists $_->{'/proc/pid/smaps'}->{$pid} and exists $_->{'/proc/pid/smaps'}->{$pid}->{'#Name'}) {
  68. $_->{'/proc/pid/smaps'}->{$pid}->{'#Name'}
  69. } else {
  70. undef
  71. }
  72. } @$masterdb;
  73. $pid_to_cmdline{$pid} = join(' / ', @cmdlines);
  74. }
  75. join(': ', $pid, $pid_to_cmdline{$pid})
  76. }
  77. sub sum_smaps {
  78. my $masterdb = shift;
  79. my $smaps_key = shift;
  80. return map {
  81. my $entry = $_;
  82. if (exists $entry->{'/proc/pid/smaps'}) {
  83. sum map {
  84. my $pid = $_;
  85. exists $entry->{'/proc/pid/smaps'}->{$pid} &&
  86. exists $entry->{'/proc/pid/smaps'}->{$pid}->{"total_${smaps_key}"} ?
  87. $entry->{'/proc/pid/smaps'}->{$pid}->{"total_${smaps_key}"} : undef
  88. } keys %{$entry->{'/proc/pid/smaps'}}
  89. } else { undef }
  90. } @$masterdb;
  91. }
  92. sub generate_plot_system_memory_1 {
  93. my $plotter = shift;
  94. my $masterdb = shift;
  95. my $plot = $plotter->new_linespoints(
  96. key => '2200_system_memory_1',
  97. label => 'System-level memory 1',
  98. legend => 'SYSTEM MEMORY 1',
  99. ylabel => 'MB',
  100. );
  101. $plot->push(
  102. [kb2mb nonzero map { $_->{'/proc/meminfo'}->{SwapTotal} -
  103. $_->{'/proc/meminfo'}->{SwapFree} } @$masterdb],
  104. lw => 5, lc => 'FF0000', title => 'Swap used',
  105. );
  106. $plot->push(
  107. [kb2mb nonzero sum_smaps($masterdb, 'Swap')],
  108. lc => 'FF0000', title => 'Sum of swapped in applications',
  109. );
  110. foreach my $key (qw/SwapCached MemFree Cached Active(file)
  111. Inactive(file) Active(anon) Inactive(anon) Shmem/) {
  112. $plot->push(
  113. [kb2mb nonzero map { $_->{'/proc/meminfo'}->{$key} } @$masterdb],
  114. title => $key,
  115. );
  116. }
  117. $plot->push(
  118. [kb2mb nonzero sum_smaps($masterdb, 'Pss')],
  119. title => 'Sum of PSS',
  120. );
  121. $plot->sort(sub { shift->[-1] });
  122. done_plotting $plot;
  123. }
  124. BEGIN { register_generator \&generate_plot_system_memory_1; }
  125. sub generate_plot_system_memory_2 {
  126. my $plotter = shift;
  127. my $masterdb = shift;
  128. my $plot = $plotter->new_linespoints(
  129. key => '2200_system_memory_2',
  130. label => 'System-level memory 2',
  131. legend => 'SYSTEM MEMORY 2',
  132. ylabel => 'MB',
  133. );
  134. foreach my $key (qw/Dirty Buffers Mlocked PageTables KernelStack
  135. SReclaimable SUnreclaim/) {
  136. $plot->push(
  137. [kb2mb nonzero map { $_->{'/proc/meminfo'}->{$key} } @$masterdb],
  138. title => {
  139. SReclaimable => 'SlabReclaimable',
  140. SUnreclaim => 'SlabUnreclaimable',
  141. }->{$key} // $key,
  142. );
  143. }
  144. $plot->push(
  145. [b2mb nonzero map {
  146. my $sum;
  147. if (exists $_->{'/usr/bin/xmeminfo'}) {
  148. foreach my $xmem_entry (@{$_->{'/usr/bin/xmeminfo'}}) {
  149. $sum += $xmem_entry->{Pixmap_mem}
  150. }
  151. }
  152. $sum;
  153. } @$masterdb],
  154. title => 'Pixmaps',
  155. );
  156. $plot->sort(sub { shift->[-1] });
  157. done_plotting $plot;
  158. }
  159. BEGIN { register_generator \&generate_plot_system_memory_2; }
  160. sub generate_plot_slab_sizes {
  161. my $plotter = shift;
  162. my $masterdb = shift;
  163. my $plot = $plotter->new_histogram(
  164. key => '2255_slabs',
  165. label => 'Kernel slab memory',
  166. legend => 'KERNEL SLABS',
  167. ylabel => 'MB',
  168. );
  169. my @slabs = uniq sort map { keys %{$_->{'/proc/slabinfo'}} } @$masterdb;
  170. foreach my $slab (@slabs) {
  171. $plot->push(
  172. [nonzero kb2mb map { $_->{'/proc/slabinfo'}->{$slab} } @$masterdb],
  173. title => $slab,
  174. );
  175. }
  176. $plot->sort(sub { shift->[-1] });
  177. done_plotting $plot;
  178. }
  179. BEGIN { register_generator \&generate_plot_slab_sizes; }
  180. sub generate_plot_slab_changes {
  181. my $plotter = shift;
  182. my $masterdb = shift;
  183. my $plot = $plotter->new_linespoints(
  184. key => '2256_slabs_changes',
  185. label => 'Kernel slab memory (changed only)',
  186. legend => 'KERNEL SLABS CHANGES',
  187. ylabel => 'MB',
  188. );
  189. my @slabs = uniq sort map { keys %{$_->{'/proc/slabinfo'}} } @$masterdb;
  190. foreach my $slab (@slabs) {
  191. $plot->push(
  192. [has_changes kb2mb nonzero map { $_->{'/proc/slabinfo'}->{$slab} } @$masterdb],
  193. title => $slab,
  194. );
  195. }
  196. $plot->sort(sub { shift->[-1] });
  197. done_plotting $plot;
  198. }
  199. BEGIN { register_generator \&generate_plot_slab_changes; }
  200. sub generate_plot_ctx_total {
  201. my $plotter = shift;
  202. my $masterdb = shift;
  203. my $plot = $plotter->new_linespoints(
  204. key => '1165_ctx_total',
  205. label => 'Voluntary + non-voluntary context switches per second per process',
  206. legend => 'CTX TOTAL',
  207. ylabel => 'count per second',
  208. );
  209. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  210. foreach my $pid (@pids) {
  211. my @total_ctx = map {
  212. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  213. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  214. exists $entry{voluntary_ctxt_switches} &&
  215. exists $entry{nonvoluntary_ctxt_switches} ?
  216. $entry{voluntary_ctxt_switches} + $entry{nonvoluntary_ctxt_switches} :
  217. undef
  218. } else { undef }
  219. } @$masterdb;
  220. $plot->push(
  221. [nonzero change_per_second $masterdb, cumulative_to_changes @total_ctx],
  222. title => pid_to_cmdline($masterdb, $pid),
  223. );
  224. }
  225. $plot->sort(sub { shift->[-1] });
  226. done_plotting $plot;
  227. }
  228. BEGIN { register_generator \&generate_plot_ctx_total; }
  229. sub generate_plot_ctx_nonvol {
  230. my $plotter = shift;
  231. my $masterdb = shift;
  232. my $plot = $plotter->new_linespoints(
  233. key => '1166_ctx_nonvolunt',
  234. label => 'Non-voluntary context switches per second per process',
  235. legend => 'CTX NON-VOLUNTARY',
  236. ylabel => 'count per second',
  237. );
  238. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  239. foreach my $pid (@pids) {
  240. my @ctx = map {
  241. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  242. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  243. exists $entry{nonvoluntary_ctxt_switches} ?
  244. $entry{nonvoluntary_ctxt_switches} : undef
  245. } else { undef }
  246. } @$masterdb;
  247. $plot->push(
  248. [nonzero change_per_second $masterdb, cumulative_to_changes @ctx],
  249. title => pid_to_cmdline($masterdb, $pid),
  250. );
  251. }
  252. done_plotting $plot->sort(sub { shift->[-1] });
  253. }
  254. BEGIN { register_generator \&generate_plot_ctx_nonvol; }
  255. sub generate_plot_ctx_vol {
  256. my $plotter = shift;
  257. my $masterdb = shift;
  258. my $plot = $plotter->new_linespoints(
  259. key => '1166_ctx_volunt',
  260. label => 'Voluntary context switches per second per process',
  261. legend => 'CTX VOLUNTARY',
  262. ylabel => 'count per second',
  263. );
  264. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  265. foreach my $pid (@pids) {
  266. my @ctx = map {
  267. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  268. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  269. exists $entry{nonvoluntary_ctxt_switches} ?
  270. $entry{nonvoluntary_ctxt_switches} : undef
  271. } else { undef }
  272. } @$masterdb;
  273. $plot->push(
  274. [nonzero change_per_second $masterdb, cumulative_to_changes @ctx],
  275. title => pid_to_cmdline($masterdb, $pid),
  276. );
  277. }
  278. done_plotting $plot->sort(sub { shift->[-1] });
  279. }
  280. BEGIN { register_generator \&generate_plot_ctx_vol; }
  281. sub generate_plot_ctx_global {
  282. my $plotter = shift;
  283. my $masterdb = shift;
  284. my $plot = $plotter->new_linespoints(
  285. key => '2060_ctx_global',
  286. label => 'left: total number of context switches\nright: number of processes in system',
  287. legend => 'CTX-SW,PROC-NUM',
  288. ylabel => 'context switches per second',
  289. y2label => 'number of processes',
  290. );
  291. $plot->push(
  292. [nonzero change_per_second $masterdb, cumulative_to_changes map {
  293. exists $_->{'/proc/stat'} &&
  294. exists $_->{'/proc/stat'}->{ctxt} ?
  295. $_->{'/proc/stat'}->{ctxt} : undef
  296. } @$masterdb],
  297. axes => 'x1y1', title => 'Context switches',
  298. );
  299. $plot->push(
  300. [nonzero map {
  301. exists $_->{'/proc/loadavg'} &&
  302. exists $_->{'/proc/loadavg'}->{all} ?
  303. $_->{'/proc/loadavg'}->{all} : undef
  304. } @$masterdb],
  305. axes => 'x2y2', title => 'Process and thread count',
  306. );
  307. done_plotting $plot;
  308. }
  309. BEGIN { register_generator \&generate_plot_ctx_global; }
  310. sub generate_plot_loadavg {
  311. my $plotter = shift;
  312. my $masterdb = shift;
  313. my $plot = $plotter->new_linespoints(
  314. key => '2005_loadavg',
  315. label => 'Load average',
  316. legend => 'LOAD AVERAGE',
  317. ylabel => 'load average',
  318. );
  319. foreach my $avg (qw/min1 min5 min15/) {
  320. $plot->push(
  321. [map { $_->{'/proc/loadavg'}->{$avg} } @{$masterdb}],
  322. title => {
  323. min1 => '1 minute average',
  324. min5 => '5 minute average',
  325. min15 => '15 minute average',
  326. }->{$avg},
  327. );
  328. }
  329. done_plotting $plot;
  330. }
  331. BEGIN { register_generator \&generate_plot_loadavg; }
  332. sub generate_plot_processes_global {
  333. my $plotter = shift;
  334. my $masterdb = shift;
  335. done_plotting $plotter->new_linespoints(
  336. key => '2050_processes_created',
  337. label => 'Processes and threads created',
  338. legend => 'PROC/THREADS CREATED',
  339. ylabel => 'count',
  340. )->push(
  341. [cumulative_to_changes map { $_->{'/proc/stat'}->{processes} } @$masterdb],
  342. title => 'Processes and threads created');
  343. }
  344. BEGIN { register_generator \&generate_plot_processes_global; }
  345. sub generate_plot_major_pagefaults {
  346. my $plotter = shift;
  347. my $masterdb = shift;
  348. my $plot = $plotter->new_linespoints(
  349. key => '1010_majorfault_%d',
  350. label => 'Major page faults per second',
  351. ylabel => 'count per second',
  352. multiple => {
  353. max_plots => 3,
  354. max_per_plot => 10,
  355. split_f => sub { max @{shift()} },
  356. split_factor => 5,
  357. legend_f => sub { 'MAJOR PAGE FAULTS &mdash; MAX ' . ceil(max @{shift()}) },
  358. },
  359. );
  360. my @pids = uniq map { keys %{$_->{'/proc/pid/stat'}} } @$masterdb;
  361. foreach my $pid (@pids) {
  362. $plot->push(
  363. [nonzero change_per_second $masterdb, cumulative_to_changes map {
  364. if (exists $_->{'/proc/pid/stat'}->{$pid}) {
  365. my %entry = split ',', $_->{'/proc/pid/stat'}->{$pid};
  366. exists $entry{majflt} ? $entry{majflt} : undef
  367. } else { undef }
  368. } @$masterdb],
  369. title => pid_to_cmdline($masterdb, $pid),
  370. );
  371. }
  372. done_plotting $plot;
  373. }
  374. BEGIN { register_generator \&generate_plot_major_pagefaults; }
  375. sub generate_plot_minor_pagefaults {
  376. my $plotter = shift;
  377. my $masterdb = shift;
  378. my $plot = $plotter->new_linespoints(
  379. key => '1011_minorfault_%d',
  380. label => 'Minor page faults per second',
  381. ylabel => 'count per second',
  382. multiple => {
  383. max_plots => 3,
  384. max_per_plot => 10,
  385. split_f => sub { max @{shift()} },
  386. split_factor => 5,
  387. legend_f => sub { 'MINOR PAGE FAULTS &mdash; MAX ' . ceil(max @{shift()}) },
  388. },
  389. );
  390. my @pids = uniq map { keys %{$_->{'/proc/pid/stat'}} } @$masterdb;
  391. foreach my $pid (@pids) {
  392. $plot->push(
  393. [nonzero change_per_second $masterdb, cumulative_to_changes map {
  394. if (exists $_->{'/proc/pid/stat'}->{$pid}) {
  395. my %entry = split ',', $_->{'/proc/pid/stat'}->{$pid};
  396. exists $entry{minflt} ? $entry{minflt} : undef
  397. } else { undef }
  398. } @$masterdb],
  399. title => pid_to_cmdline($masterdb, $pid),
  400. );
  401. }
  402. done_plotting $plot;
  403. }
  404. BEGIN { register_generator \&generate_plot_minor_pagefaults; }
  405. sub generate_plot_cpu {
  406. my $plotter = shift;
  407. my $masterdb = shift;
  408. my $plot = $plotter->new_histogram(
  409. key => '2015_cpu',
  410. label => 'CPU utilization',
  411. legend => 'CPU UTILIZATION',
  412. ylabel => 'percent',
  413. );
  414. my @cpu_keys = qw/idle iowait nice user softirq irq sys/;
  415. foreach my $key (@cpu_keys) {
  416. $plot->push(
  417. [nonzero cumulative_to_changes map {
  418. my @datakeys = qw/user nice sys idle iowait irq softirq/;
  419. my $h = { zip @datakeys, @{$_->{'/proc/stat'}->{cpu}} };
  420. $h->{$key}
  421. } @$masterdb],
  422. lc => {
  423. user => "3149BD",
  424. nice => "4265FF",
  425. sys => "DE2821",
  426. idle => "ADE739",
  427. iowait => "EE00FF",
  428. irq => "FF0000",
  429. softirq => "EF0000",
  430. }->{$key},
  431. title => $key,
  432. );
  433. }
  434. $plot->scale(to => 100);
  435. done_plotting $plot;
  436. }
  437. BEGIN { register_generator \&generate_plot_cpu; }
  438. sub generate_plot_cpu_freq {
  439. my $plotter = shift;
  440. my $masterdb = shift;
  441. my @cpus = uniq map { keys %{$_->{'/sys/devices/system/cpu'} // {}} } @$masterdb;
  442. foreach my $cpu_num (@cpus) {
  443. my $plot = $plotter->new_histogram(
  444. key => "2010_cpu${cpu_num}_time_in_state",
  445. label => "CPU${cpu_num} time in state",
  446. legend => "CPU${cpu_num} TIME IN STATE",
  447. ylabel => 'percent',
  448. );
  449. my @freqs = uniq map { keys %{$_->{'/sys/devices/system/cpu'}->{$cpu_num}->{cpufreq}->{stats}->{time_in_state}} } @$masterdb;
  450. foreach my $freq (sort { $b <=> $a } @freqs) {
  451. $plot->push(
  452. [cumulative_to_changes
  453. map { $_->{'/sys/devices/system/cpu'}->{$cpu_num}->{cpufreq}->{stats}->{time_in_state}->{$freq} } @$masterdb],
  454. title => int($freq/1000) . 'MHz',
  455. );
  456. }
  457. $plot->scale(to => 100);
  458. done_plotting $plot;
  459. }
  460. }
  461. BEGIN { register_generator \&generate_plot_cpu_freq; }
  462. sub fs_to_mountpoint {
  463. my $fs = shift;
  464. my $masterdb = shift;
  465. my @mountpoints = uniq map { keys %{$_->{'/bin/df'} // {}} } @$masterdb;
  466. foreach my $mountpoint (@mountpoints) {
  467. my ($filesystem) = uniq map { $_->{'/bin/df'}->{$mountpoint}->{filesystem} } @$masterdb;
  468. if ($filesystem =~ /\b\Q$fs\E\b/) {
  469. return $fs . ': ' . $mountpoint;
  470. }
  471. }
  472. return $fs;
  473. }
  474. sub generate_plot_fs_written {
  475. my $plotter = shift;
  476. my $masterdb = shift;
  477. my $plot = $plotter->new_linespoints(
  478. key => '2102_ext4_written',
  479. label => 'Bytes written to ext4 partitions (excluding non-changed)',
  480. legend => 'EXT4 WRITES',
  481. ylabel => 'MB',
  482. );
  483. my @filesystems = uniq map { keys %{$_->{'/sys/fs/ext4'}} } @$masterdb;
  484. foreach my $fs (@filesystems) {
  485. $plot->push(
  486. [kb2mb nonzero has_changes cumulative_to_changes map {
  487. exists $_->{'/sys/fs/ext4'}->{$fs} ? $_->{'/sys/fs/ext4'}->{$fs}->{lifetime_write_kbytes} : undef
  488. } @$masterdb],
  489. title => fs_to_mountpoint($fs, $masterdb),
  490. );
  491. }
  492. done_plotting $plot;
  493. }
  494. BEGIN { register_generator \&generate_plot_fs_written; }
  495. sub generate_plot_cputime {
  496. my $plotter = shift;
  497. my $masterdb = shift;
  498. my $plot = $plotter->new_linespoints(
  499. key => '1150_cpu_user_sys_time_%d',
  500. label => 'CPU user+sys time',
  501. ylabel => 'percent',
  502. multiple => {
  503. max_plots => 2,
  504. max_per_plot => 20,
  505. split_f => sub { max @{shift()} },
  506. split_factor => 5,
  507. legend_f => sub { 'CPU TIME &mdash; USER+SYS &mdash; MAX ' . ceil(max @{shift()}) . '%' },
  508. },
  509. );
  510. my @pids = uniq map { keys %{$_->{'/proc/pid/stat'}} } @$masterdb;
  511. foreach my $pid (@pids) {
  512. my @entry = change_per_second $masterdb, cumulative_to_changes map {
  513. if (exists $_->{'/proc/pid/stat'}->{$pid}) {
  514. my %entry = split ',', $_->{'/proc/pid/stat'}->{$pid};
  515. exists $entry{utime} && exists $entry{stime} ?
  516. $entry{utime} + $entry{stime} : undef
  517. } else { undef }
  518. } @$masterdb;
  519. next unless any { defined && $_ > 0 } @entry;
  520. if (CLK_TCK != 100) {
  521. foreach (0 .. @entry-1) {
  522. $entry[$_] /= CLK_TCK;
  523. $entry[$_] *= 100;
  524. }
  525. }
  526. $plot->push([nonzero @entry], title => pid_to_cmdline($masterdb, $pid));
  527. }
  528. done_plotting $plot;
  529. }
  530. BEGIN { register_generator \&generate_plot_cputime; }
  531. sub generate_plot_cputime_user {
  532. my $plotter = shift;
  533. my $masterdb = shift;
  534. my $plot = $plotter->new_linespoints(
  535. key => '1160_cpu_usertime_%d',
  536. label => 'CPU user time',
  537. ylabel => 'percent',
  538. multiple => {
  539. max_plots => 2,
  540. max_per_plot => 20,
  541. split_f => sub { max @{shift()} },
  542. split_factor => 5,
  543. legend_f => sub { 'CPU TIME &mdash; USER &mdash; MAX ' . ceil(max @{shift()}) . '%' },
  544. },
  545. );
  546. my @pids = uniq map { keys %{$_->{'/proc/pid/stat'}} } @$masterdb;
  547. foreach my $pid (@pids) {
  548. my @entry = change_per_second $masterdb, cumulative_to_changes map {
  549. if (exists $_->{'/proc/pid/stat'}->{$pid}) {
  550. my %entry = split ',', $_->{'/proc/pid/stat'}->{$pid};
  551. exists $entry{utime} ? $entry{utime} : undef
  552. } else { undef }
  553. } @$masterdb;
  554. next unless any { defined && $_ > 0 } @entry;
  555. if (CLK_TCK != 100) {
  556. foreach (0 .. @entry-1) {
  557. $entry[$_] /= CLK_TCK;
  558. $entry[$_] *= 100;
  559. }
  560. }
  561. $plot->push([nonzero @entry], title => pid_to_cmdline($masterdb, $pid));
  562. }
  563. done_plotting $plot;
  564. }
  565. BEGIN { register_generator \&generate_plot_cputime_user; }
  566. sub generate_plot_cputime_sys {
  567. my $plotter = shift;
  568. my $masterdb = shift;
  569. my $plot = $plotter->new_linespoints(
  570. key => '1162_cpu_systime_%d',
  571. label => 'CPU sys time',
  572. ylabel => 'percent',
  573. multiple => {
  574. max_plots => 2,
  575. max_per_plot => 20,
  576. split_f => sub { max @{shift()} },
  577. split_factor => 5,
  578. legend_f => sub { 'CPU TIME &mdash; SYS &mdash; MAX ' . ceil(max @{shift()}) . '%' },
  579. },
  580. );
  581. my @pids = uniq map { keys %{$_->{'/proc/pid/stat'}} } @$masterdb;
  582. foreach my $pid (@pids) {
  583. my @entry = change_per_second $masterdb, cumulative_to_changes map {
  584. if (exists $_->{'/proc/pid/stat'}->{$pid}) {
  585. my %entry = split ',', $_->{'/proc/pid/stat'}->{$pid};
  586. exists $entry{stime} ? $entry{stime} : undef
  587. } else { undef }
  588. } @$masterdb;
  589. next unless any { defined && $_ > 0 } @entry;
  590. if (CLK_TCK != 100) {
  591. foreach (0 .. @entry-1) {
  592. $entry[$_] /= CLK_TCK;
  593. $entry[$_] *= 100;
  594. }
  595. }
  596. $plot->push([nonzero @entry], title => pid_to_cmdline($masterdb, $pid));
  597. }
  598. done_plotting $plot;
  599. }
  600. BEGIN { register_generator \&generate_plot_cputime_sys; }
  601. sub sysvipc {
  602. my $masterdb = shift;
  603. my $type = shift;
  604. my $key = shift;
  605. map {
  606. exists $_->{"/proc/sysvipc/$type"} &&
  607. exists $_->{"/proc/sysvipc/$type"}->{$key} ?
  608. $_->{"/proc/sysvipc/$type"}->{$key} : undef
  609. } @$masterdb;
  610. }
  611. sub generate_plot_sysvipc_count {
  612. my $plotter = shift;
  613. my $masterdb = shift;
  614. my $plot = $plotter->new_linespoints(
  615. key => '2241_sysvipc_count',
  616. label => 'SysV IPC object counts:\n-Shared memory segments (SHM)\n-Message queues (MSG)\n-Semaphore sets (SEM)',
  617. legend => 'SYSV SHM+MSG+SEM COUNT',
  618. ylabel => 'count',
  619. );
  620. my @nattch0 = sysvipc $masterdb, 'shm', 'nattch0';
  621. my @nattch1 = sysvipc $masterdb, 'shm', 'nattch1';
  622. my @nattch2 = sysvipc $masterdb, 'shm', 'nattch2';
  623. my @nattch3 = sysvipc $masterdb, 'shm', 'nattch3';
  624. my @msg = sysvipc $masterdb, 'msg', 'count';
  625. my @sem = sysvipc $masterdb, 'sem', 'count';
  626. if (nonzero(@nattch0) > 0 or nonzero(@nattch1) > 0 or nonzero(@nattch2) > 0 or
  627. nonzero(@nattch3) > 0 or nonzero(@msg) > 0 or nonzero(@sem) > 0) {
  628. $plot->push([nonzero @nattch0], title => 'SHM - 0 processes attached');
  629. $plot->push([nonzero @nattch1], title => 'SHM - 1 process attached');
  630. $plot->push([nonzero @nattch2], title => 'SHM - 2 processes attached');
  631. $plot->push([nonzero @nattch3], title => 'SHM - 3+ processes attached');
  632. $plot->push([nonzero @msg], title => 'MSG');
  633. $plot->push([nonzero @sem], title => 'SEM');
  634. }
  635. done_plotting $plot;
  636. }
  637. BEGIN { register_generator \&generate_plot_sysvipc_count; }
  638. sub generate_plot_sysvipc_locked_size {
  639. my $plotter = shift;
  640. my $masterdb = shift;
  641. my $plot = $plotter->new_histogram(
  642. key => '2240_sysvipc_locked_unlocked_size',
  643. label => 'SysV shared memory locked+unlocked Size sum',
  644. legend => 'SYSV SHM LOCKED+UNLOCKED',
  645. ylabel => 'MB',
  646. );
  647. my @locked = sysvipc $masterdb, 'shm', 'size_locked';
  648. my @unlocked = sysvipc $masterdb, 'shm', 'size_unlocked';
  649. if (nonzero(@locked) > 0 or nonzero(@unlocked) > 0) {
  650. $plot->push([b2mb @locked], title => 'Locked to memory');
  651. $plot->push([b2mb @unlocked], title => 'Unlocked');
  652. }
  653. done_plotting $plot;
  654. }
  655. BEGIN { register_generator \&generate_plot_sysvipc_locked_size; }
  656. sub generate_plot_sysvipc_shm_cpid {
  657. my $plotter = shift;
  658. my $masterdb = shift;
  659. my $plot = $plotter->new_histogram(
  660. key => '1052_sysvipc_shm_cpid',
  661. label => 'SysV shared memory Size per Creator PID (CPID)',
  662. legend => 'SYSV SHM SIZE PER CPID',
  663. ylabel => 'MB',
  664. );
  665. my @cpids = uniq grep { defined && length } map {
  666. exists $_->{'/proc/sysvipc/shm'} &&
  667. exists $_->{'/proc/sysvipc/shm'}->{cpid_to_size} ?
  668. keys %{$_->{'/proc/sysvipc/shm'}->{cpid_to_size}} : undef
  669. } @$masterdb;
  670. foreach my $cpid (@cpids) {
  671. $plot->push(
  672. [nonzero b2mb map {
  673. exists $_->{'/proc/sysvipc/shm'} &&
  674. exists $_->{'/proc/sysvipc/shm'}->{cpid_to_size} &&
  675. exists $_->{'/proc/sysvipc/shm'}->{cpid_to_size}->{$cpid} ?
  676. $_->{'/proc/sysvipc/shm'}->{cpid_to_size}->{$cpid} : undef
  677. } @$masterdb],
  678. title => pid_to_cmdline($masterdb, $cpid),
  679. );
  680. }
  681. done_plotting $plot;
  682. }
  683. BEGIN { register_generator \&generate_plot_sysvipc_shm_cpid; }
  684. sub generate_plot_mlocked {
  685. my $plotter = shift;
  686. my $masterdb = shift;
  687. my $plot = $plotter->new_histogram(
  688. key => '1020_locked',
  689. label => 'VmLck per process',
  690. legend => 'LOCKED',
  691. ylabel => 'MB',
  692. );
  693. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  694. foreach my $pid (@pids) {
  695. $plot->push(
  696. [kb2mb nonzero map {
  697. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  698. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  699. exists $entry{VmLck} ? $entry{VmLck} : undef
  700. } else { undef }
  701. } @$masterdb],
  702. title => pid_to_cmdline($masterdb, $pid),
  703. );
  704. }
  705. done_plotting $plot->sort(sub { shift->[-1] });
  706. }
  707. BEGIN { register_generator \&generate_plot_mlocked; }
  708. sub generate_plot_vmsize {
  709. my $plotter = shift;
  710. my $masterdb = shift;
  711. my $plot = $plotter->new_linespoints(
  712. key => '1040_vmsize_%d',
  713. label => 'Process virtual memory size (excluding non-changed)',
  714. ylabel => 'MB',
  715. multiple => {
  716. max_plots => 4,
  717. max_per_plot => 15,
  718. split_f => sub { max @{shift()} },
  719. split_factor => 5,
  720. legend_f => sub { 'VMSIZE &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  721. },
  722. );
  723. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  724. foreach my $pid (@pids) {
  725. $plot->push(
  726. [nonzero kb2mb has_changes map {
  727. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  728. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  729. exists $entry{VmSize} ? $entry{VmSize} : undef
  730. } else { undef }
  731. } @$masterdb],
  732. title => pid_to_cmdline($masterdb, $pid),
  733. );
  734. }
  735. done_plotting $plot;
  736. }
  737. BEGIN { register_generator \&generate_plot_vmsize; }
  738. sub generate_plot_memory_map_count {
  739. my $plotter = shift;
  740. my $masterdb = shift;
  741. my $plot = $plotter->new_linespoints(
  742. key => '1045_num_mmaps_%d',
  743. label => 'Number of memory maps (virtual memory areas)',
  744. ylabel => 'count',
  745. multiple => {
  746. max_plots => 3,
  747. max_per_plot => 15,
  748. split_f => sub { max @{shift()} },
  749. split_factor => 5,
  750. legend_f => sub { '#MEMORY MAPS &mdash; MAX ' . max @{shift()} },
  751. },
  752. );
  753. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  754. foreach my $pid (@pids) {
  755. $plot->push(
  756. [nonzero has_changes map {
  757. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  758. exists $_->{'/proc/pid/smaps'}->{$pid}->{vmacount} ?
  759. $_->{'/proc/pid/smaps'}->{$pid}->{vmacount} : undef
  760. } @$masterdb],
  761. title => pid_to_cmdline($masterdb, $pid),
  762. );
  763. }
  764. done_plotting $plot;
  765. }
  766. BEGIN { register_generator \&generate_plot_memory_map_count; }
  767. sub private_dirty_collect_data {
  768. my $plot = shift;
  769. my $masterdb = shift;
  770. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  771. #print Dumper \@pids;
  772. foreach my $pid (@pids) {
  773. $plot->push(
  774. [kb2mb nonzero map {
  775. if (exists $_->{'/proc/pid/smaps'}->{$pid} &&
  776. (exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Private_Dirty} or
  777. exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Swap})) {
  778. my $private_dirty = 0;
  779. my $swap = 0;
  780. if (exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Private_Dirty}) {
  781. $private_dirty = $_->{'/proc/pid/smaps'}->{$pid}->{total_Private_Dirty};
  782. }
  783. if (exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Swap}) {
  784. $swap = $_->{'/proc/pid/smaps'}->{$pid}->{total_Swap};
  785. }
  786. $private_dirty + $swap
  787. } else { undef }
  788. } @$masterdb],
  789. title => pid_to_cmdline($masterdb, $pid),
  790. );
  791. }
  792. return $plot;
  793. }
  794. sub generate_plot_private_dirty {
  795. my $plotter = shift;
  796. my $masterdb = shift;
  797. my $plot = $plotter->new_histogram(
  798. key => '1009_private_dirty_plus_swap',
  799. label => 'Private dirty + swap',
  800. legend => 'PRIVATE DIRTY+SWAP',
  801. ylabel => 'MB',
  802. column_limit => 1,
  803. reduce_f => sub {
  804. my @leftovers;
  805. foreach my $idx (0 .. @$masterdb-1) {
  806. push @leftovers, sum map {
  807. exists $_->{__data} &&
  808. exists $_->{__data}->[$idx] ?
  809. $_->{__data}->[$idx] : undef
  810. } @_;
  811. }
  812. return [nonzero @leftovers],
  813. title => 'Sum of ' . scalar(@_) . ' processes';
  814. },
  815. );
  816. private_dirty_collect_data $plot, $masterdb;
  817. $plot->sort(sub { max @{shift()} });
  818. $plot->reduce;
  819. $plot->sort(\&max_change, sub { max @{shift()} });
  820. done_plotting $plot;
  821. }
  822. BEGIN { register_generator \&generate_plot_private_dirty; }
  823. sub generate_plot_private_dirty_changes {
  824. my $plotter = shift;
  825. my $masterdb = shift;
  826. my $plot = $plotter->new_linespoints(
  827. key => '1009_private_dirty_plus_swap_%d',
  828. label => 'Private dirty + swap (excluding non-changed)',
  829. ylabel => 'MB',
  830. multiple => {
  831. max_plots => 4,
  832. max_per_plot => 15,
  833. split_f => sub { max @{shift()} },
  834. split_factor => 5,
  835. legend_f => sub { 'PRIVATE DIRTY+SWAP &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  836. },
  837. exclude_nonchanged => 1,
  838. );
  839. private_dirty_collect_data $plot, $masterdb;
  840. done_plotting $plot;
  841. }
  842. BEGIN { register_generator \&generate_plot_private_dirty_changes; }
  843. sub generate_plot_heap_histogram {
  844. my $plotter = shift;
  845. my $masterdb = shift;
  846. my $plot = $plotter->new_histogram(
  847. key => '1001_heap',
  848. label => 'Heap Size per process',
  849. legend => 'HEAP SIZE',
  850. ylabel => 'MB',
  851. column_limit => 1,
  852. reduce_f => sub {
  853. my @leftovers;
  854. foreach my $idx (0 .. @$masterdb-1) {
  855. push @leftovers, sum map {
  856. exists $_->{__data} &&
  857. exists $_->{__data}->[$idx] ?
  858. $_->{__data}->[$idx] : undef
  859. } @_;
  860. }
  861. return [nonzero @leftovers],
  862. title => 'Sum of ' . scalar(@_) . ' process heaps';
  863. },
  864. );
  865. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  866. foreach my $pid (@pids) {
  867. $plot->push([kb2mb nonzero map {
  868. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  869. exists $_->{'/proc/pid/smaps'}->{$pid}->{'[heap]'} &&
  870. exists $_->{'/proc/pid/smaps'}->{$pid}->{'[heap]'}->{total_Size} ?
  871. $_->{'/proc/pid/smaps'}->{$pid}->{'[heap]'}->{total_Size} : undef
  872. } @$masterdb],
  873. title => pid_to_cmdline($masterdb, $pid),
  874. );
  875. }
  876. $plot->sort(sub { max @{shift()} });
  877. $plot->reduce;
  878. $plot->sort(\&max_change, sub { max @{shift()} });
  879. done_plotting $plot;
  880. }
  881. BEGIN { register_generator \&generate_plot_heap_histogram; }
  882. sub generate_plot_heap_changes {
  883. my $plotter = shift;
  884. my $masterdb = shift;
  885. my $plot = $plotter->new_linespoints(
  886. key => '1001_heap_changes_%d',
  887. label => 'Heap Size per process (excluding non-changed)',
  888. ylabel => 'MB',
  889. multiple => {
  890. max_plots => 5,
  891. max_per_plot => 10,
  892. split_f => sub { max @{shift()} },
  893. split_factor => 5,
  894. legend_f => sub { 'HEAP SIZE &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  895. },
  896. );
  897. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  898. foreach my $pid (@pids) {
  899. $plot->push([kb2mb has_changes nonzero map {
  900. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  901. exists $_->{'/proc/pid/smaps'}->{$pid}->{'[heap]'} &&
  902. exists $_->{'/proc/pid/smaps'}->{$pid}->{'[heap]'}->{total_Size} ?
  903. $_->{'/proc/pid/smaps'}->{$pid}->{'[heap]'}->{total_Size} : undef
  904. } @$masterdb],
  905. title => pid_to_cmdline($masterdb, $pid),
  906. );
  907. }
  908. done_plotting $plot;
  909. }
  910. BEGIN { register_generator \&generate_plot_heap_changes; }
  911. sub generate_plot_sysvipc_shm_size {
  912. my $plotter = shift;
  913. my $masterdb = shift;
  914. my $plot = $plotter->new_histogram(
  915. key => '1050_sysvipc_shm_size',
  916. label => 'SysV shared memory segment total Size per process',
  917. legend => 'SYSV SHM SIZE',
  918. ylabel => 'MB',
  919. );
  920. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'}} } @$masterdb;
  921. foreach my $pid (@pids) {
  922. $plot->push(
  923. [kb2mb nonzero map {
  924. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  925. exists $_->{'/proc/pid/smaps'}->{$pid}->{'/SYSV'} &&
  926. exists $_->{'/proc/pid/smaps'}->{$pid}->{'/SYSV'}->{total_Size} ?
  927. $_->{'/proc/pid/smaps'}->{$pid}->{'/SYSV'}->{total_Size} : undef
  928. } @$masterdb],
  929. title => pid_to_cmdline($masterdb, $pid),
  930. );
  931. }
  932. done_plotting $plot->sort(sub { shift->[-1] });
  933. }
  934. BEGIN { register_generator \&generate_plot_sysvipc_shm_size; }
  935. sub generate_plot_posix_shm_size {
  936. my $plotter = shift;
  937. my $masterdb = shift;
  938. my $plot = $plotter->new_histogram(
  939. key => '1051_posixipc_shm_size',
  940. label => 'POSIX shared memory segment total Size per process',
  941. legend => 'POSIX SHM SIZE',
  942. ylabel => 'MB',
  943. );
  944. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'}} } @$masterdb;
  945. foreach my $pid (@pids) {
  946. $plot->push(
  947. [kb2mb nonzero map {
  948. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  949. exists $_->{'/proc/pid/smaps'}->{$pid}->{'/dev/shm/'} &&
  950. exists $_->{'/proc/pid/smaps'}->{$pid}->{'/dev/shm/'}->{total_Size} ?
  951. $_->{'/proc/pid/smaps'}->{$pid}->{'/dev/shm/'}->{total_Size} : undef
  952. } @$masterdb],
  953. title => pid_to_cmdline($masterdb, $pid),
  954. );
  955. }
  956. done_plotting $plot->sort(sub { shift->[-1] });
  957. }
  958. BEGIN { register_generator \&generate_plot_posix_shm_size; }
  959. sub generate_plot_gfx_mmap_size {
  960. my $plotter = shift;
  961. my $masterdb = shift;
  962. foreach my $gfx_mmap (@SP::Endurance::Parser::GFX_MMAPS) {
  963. my $plot = $plotter->new_histogram(
  964. key => '1060_gfx_mmap_size' . (($_ = $gfx_mmap) =~ s#/#_#g, $_),
  965. label => "Total Size of $gfx_mmap memory mappings per process",
  966. legend => "$gfx_mmap MMAP SIZE",
  967. ylabel => 'MB',
  968. );
  969. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  970. foreach my $pid (@pids) {
  971. $plot->push([kb2mb nonzero map {
  972. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  973. exists $_->{'/proc/pid/smaps'}->{$pid}->{$gfx_mmap} &&
  974. exists $_->{'/proc/pid/smaps'}->{$pid}->{$gfx_mmap}->{total_Size} ?
  975. $_->{'/proc/pid/smaps'}->{$pid}->{$gfx_mmap}->{total_Size} : undef
  976. } @$masterdb],
  977. title => pid_to_cmdline($masterdb, $pid),
  978. );
  979. }
  980. $plot->sort(sub { shift->[-1] });
  981. done_plotting $plot;
  982. }
  983. }
  984. BEGIN { register_generator \&generate_plot_gfx_mmap_size; }
  985. sub generate_plot_gfx_mmap_count {
  986. my $plotter = shift;
  987. my $masterdb = shift;
  988. foreach my $gfx_mmap (@SP::Endurance::Parser::GFX_MMAPS) {
  989. my $plot = $plotter->new_linespoints(
  990. key => '1061_gfx_mmap_count' . (($_ = $gfx_mmap) =~ s#/#_#g, $_),
  991. label => "Count of $gfx_mmap memory mappings per process (excluding non-changed)",
  992. legend => "$gfx_mmap MMAP COUNT",
  993. ylabel => 'count',
  994. );
  995. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  996. foreach my $pid (@pids) {
  997. $plot->push([has_changes nonzero map {
  998. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  999. exists $_->{'/proc/pid/smaps'}->{$pid}->{$gfx_mmap} &&
  1000. exists $_->{'/proc/pid/smaps'}->{$pid}->{$gfx_mmap}->{vmacount} ?
  1001. $_->{'/proc/pid/smaps'}->{$pid}->{$gfx_mmap}->{vmacount} : undef
  1002. } @$masterdb],
  1003. title => pid_to_cmdline($masterdb, $pid),
  1004. );
  1005. }
  1006. $plot->sort(sub { shift->[-1] });
  1007. done_plotting $plot;
  1008. }
  1009. }
  1010. BEGIN { register_generator \&generate_plot_gfx_mmap_count; }
  1011. sub generate_plot_rwxp_mmap_size {
  1012. my $plotter = shift;
  1013. my $masterdb = shift;
  1014. my $plot = $plotter->new_histogram(
  1015. key => '1030_rwxp_mmap_size',
  1016. label => q/Total Size of memory mappings with 'rwxp' protection flags./,
  1017. legend => 'WRITABLE-EXEC MMAP SIZE',
  1018. ylabel => 'MB',
  1019. );
  1020. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  1021. foreach my $pid (@pids) {
  1022. $plot->push([kb2mb nonzero map { my $entry = $_;
  1023. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  1024. exists $_->{'/proc/pid/smaps'}->{$pid}->{rwxp} &&
  1025. exists $_->{'/proc/pid/smaps'}->{$pid}->{rwxp}->{total_Size} ?
  1026. $_->{'/proc/pid/smaps'}->{$pid}->{rwxp}->{total_Size} : undef
  1027. } @$masterdb],
  1028. title => pid_to_cmdline($masterdb, $pid),
  1029. );
  1030. }
  1031. $plot->sort(\&max_change, sub { max @{shift()} });
  1032. done_plotting $plot;
  1033. }
  1034. BEGIN { register_generator \&generate_plot_rwxp_mmap_size; }
  1035. sub generate_plot_pss {
  1036. my $plotter = shift;
  1037. my $masterdb = shift;
  1038. my $plot = $plotter->new_histogram(
  1039. key => '1006_pss',
  1040. label => 'Proportional Set Size (PSS) total per process',
  1041. legend => 'PSS',
  1042. ylabel => 'MB',
  1043. column_limit => 1,
  1044. reduce_f => sub {
  1045. my @leftovers;
  1046. foreach my $idx (0 .. @$masterdb-1) {
  1047. push @leftovers, sum map {
  1048. exists $_->{__data} &&
  1049. exists $_->{__data}->[$idx] ?
  1050. $_->{__data}->[$idx] : undef
  1051. } @_;
  1052. }
  1053. return [nonzero @leftovers],
  1054. title => 'Sum of ' . scalar(@_) . ' process PSS';
  1055. },
  1056. );
  1057. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  1058. foreach my $pid (@pids) {
  1059. $plot->push(
  1060. [kb2mb nonzero map {
  1061. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  1062. exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} ?
  1063. $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} : undef
  1064. } @$masterdb],
  1065. title => pid_to_cmdline($masterdb, $pid),
  1066. );
  1067. }
  1068. $plot->sort(sub { max @{shift()} });
  1069. $plot->reduce;
  1070. $plot->sort(\&max_change, sub { max @{shift()} });
  1071. done_plotting $plot;
  1072. }
  1073. BEGIN { register_generator \&generate_plot_pss; }
  1074. sub generate_plot_pss_only_changes {
  1075. my $plotter = shift;
  1076. my $masterdb = shift;
  1077. my $plot = $plotter->new_linespoints(
  1078. key => '1006_pss_changes_%d',
  1079. label => 'Proportional Set Size (PSS) per process (excluding non-changed)',
  1080. ylabel => 'MB',
  1081. multiple => {
  1082. max_plots => 4,
  1083. max_per_plot => 10,
  1084. split_f => sub { max @{shift()} },
  1085. split_factor => 5,
  1086. legend_f => sub { 'PSS &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  1087. },
  1088. );
  1089. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  1090. foreach my $pid (@pids) {
  1091. $plot->push(
  1092. [kb2mb nonzero has_changes map {
  1093. exists $_->{'/proc/pid/smaps'}->{$pid} &&
  1094. exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} ?
  1095. $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} : undef
  1096. } @$masterdb],
  1097. title => pid_to_cmdline($masterdb, $pid),
  1098. );
  1099. }
  1100. done_plotting $plot;
  1101. }
  1102. sub generate_plot_pss_swap_changes {
  1103. my $plotter = shift;
  1104. my $masterdb = shift;
  1105. my $plot = $plotter->new_linespoints(
  1106. key => '1006_pss_swap_changes_%d',
  1107. label => 'Proportional Set Size (PSS) + Swap per process (excluding non-changed)',
  1108. ylabel => 'MB',
  1109. multiple => {
  1110. max_plots => 4,
  1111. max_per_plot => 10,
  1112. split_f => sub { max @{shift()} },
  1113. split_factor => 5,
  1114. legend_f => sub { 'PSS+SWAP &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  1115. },
  1116. );
  1117. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  1118. foreach my $pid (@pids) {
  1119. $plot->push(
  1120. [kb2mb nonzero has_changes map {
  1121. if (exists $_->{'/proc/pid/smaps'}->{$pid} and
  1122. (exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} or
  1123. exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Swap})) {
  1124. (exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} ?
  1125. $_->{'/proc/pid/smaps'}->{$pid}->{total_Pss} : 0) +
  1126. (exists $_->{'/proc/pid/smaps'}->{$pid}->{total_Swap} ?
  1127. $_->{'/proc/pid/smaps'}->{$pid}->{total_Swap} : 0)
  1128. } else { undef }
  1129. } @$masterdb],
  1130. title => pid_to_cmdline($masterdb, $pid),
  1131. );
  1132. }
  1133. done_plotting $plot;
  1134. }
  1135. sub generate_plot_pss_changes {
  1136. my $plotter = shift;
  1137. my $masterdb = shift;
  1138. my @pids = uniq map { keys %{$_->{'/proc/pid/smaps'} // {}} } @$masterdb;
  1139. foreach my $entry (@$masterdb) {
  1140. foreach my $pid (@pids) {
  1141. goto swap if exists $entry->{'/proc/pid/smaps'}->{$pid} and
  1142. exists $entry->{'/proc/pid/smaps'}->{$pid}->{total_Swap} and
  1143. $entry->{'/proc/pid/smaps'}->{$pid}->{total_Swap};
  1144. }
  1145. }
  1146. return generate_plot_pss_only_changes $plotter, $masterdb;
  1147. swap:
  1148. return generate_plot_pss_swap_changes $plotter, $masterdb;
  1149. }
  1150. BEGIN { register_generator \&generate_plot_pss_changes; }
  1151. sub generate_plot_threads {
  1152. my $plotter = shift;
  1153. my $masterdb = shift;
  1154. my $plot = $plotter->new_histogram(
  1155. key => '1200_threads_count',
  1156. label => 'Number of threads per process (single threaded processes excluded)',
  1157. legend => 'THREAD COUNT',
  1158. ylabel => 'thread count',
  1159. column_limit => 1,
  1160. reduce_f => sub {
  1161. my @leftovers;
  1162. foreach my $idx (0 .. @$masterdb-1) {
  1163. push @leftovers, sum map {
  1164. exists $_->{__data} &&
  1165. exists $_->{__data}->[$idx] ?
  1166. $_->{__data}->[$idx] : undef
  1167. } @_;
  1168. }
  1169. return [nonzero @leftovers],
  1170. title => 'Sum of ' . scalar(@_) . ' process threads';
  1171. },
  1172. );
  1173. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  1174. foreach my $pid (@pids) {
  1175. my @threads = map {
  1176. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  1177. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  1178. exists $entry{Threads} ? $entry{Threads} : undef
  1179. } else { undef }
  1180. } @$masterdb;
  1181. next unless any { defined and $_ > 1 } @threads;
  1182. $plot->push(
  1183. [nonzero @threads],
  1184. title => pid_to_cmdline($masterdb, $pid),
  1185. );
  1186. }
  1187. $plot->sort(sub { max @{shift()} });
  1188. $plot->reduce;
  1189. $plot->sort(\&max_change, sub { max @{shift()} });
  1190. done_plotting $plot;
  1191. }
  1192. BEGIN { register_generator \&generate_plot_threads; }
  1193. sub generate_plot_threads_changes {
  1194. my $plotter = shift;
  1195. my $masterdb = shift;
  1196. my $plot = $plotter->new_linespoints(
  1197. key => '1201_threads_changes',
  1198. label => 'Number of threads per process (non-changed and single threaded processes excluded)',
  1199. legend => 'THREAD CHANGES',
  1200. ylabel => 'thread count',
  1201. );
  1202. my @pids = uniq map { keys %{$_->{'/proc/pid/status'}} } @$masterdb;
  1203. foreach my $pid (@pids) {
  1204. my @threads = map {
  1205. if (exists $_->{'/proc/pid/status'}->{$pid}) {
  1206. my %entry = split ',', $_->{'/proc/pid/status'}->{$pid};
  1207. exists $entry{Threads} ? $entry{Threads} : undef
  1208. } else { undef }
  1209. } @$masterdb;
  1210. next unless any { defined and $_ > 1 } @threads;
  1211. $plot->push(
  1212. [has_changes nonzero @threads],
  1213. title => pid_to_cmdline($masterdb, $pid),
  1214. );
  1215. }
  1216. done_plotting $plot->sort(sub { shift->[-1] });
  1217. }
  1218. BEGIN { register_generator \&generate_plot_threads_changes; }
  1219. sub x11_pid_to_identifier {
  1220. my $pid = shift;
  1221. my $masterdb = shift;
  1222. uniq sort grep { defined && length } map { my $entry = $_;
  1223. exists $entry->{'/usr/bin/xmeminfo'} ?
  1224. (map { $_->{Identifier} } grep { $_->{PID} == $pid } @{$entry->{'/usr/bin/xmeminfo'}}) :
  1225. undef
  1226. } @$masterdb;
  1227. }
  1228. sub generate_plot_x11_resource_count {
  1229. my $plotter = shift;
  1230. my $masterdb = shift;
  1231. my $plot = $plotter->new_linespoints(
  1232. key => '1071_x11_resource_count_%d',
  1233. label => 'X11 total resource count per process (excluding non-changed)',
  1234. ylabel => 'count',
  1235. multiple => {
  1236. max_plots => 3,
  1237. max_per_plot => 10,
  1238. split_f => sub { max @{shift()} },
  1239. split_factor => 5,
  1240. legend_f => sub { 'X11 RESOURCE COUNT &mdash; MAX ' . max @{shift()} },
  1241. },
  1242. );
  1243. my @pids = uniq grep { defined && length } map { $_->{PID} } map {
  1244. exists $_->{'/usr/bin/xmeminfo'} ? @{$_->{'/usr/bin/xmeminfo'} // []} : undef
  1245. } @$masterdb;
  1246. foreach my $pid (@pids) {
  1247. my @total_resource_count = map { my $entry = $_;
  1248. exists $entry->{'/usr/bin/xmeminfo'} ?
  1249. (sum map { $_->{total_resource_count} } grep { $_->{PID} == $pid } @{$entry->{'/usr/bin/xmeminfo'}}) :
  1250. undef
  1251. } @$masterdb;
  1252. my $identifier = join ' / ', x11_pid_to_identifier($pid, $masterdb);
  1253. $plot->push([nonzero has_changes @total_resource_count],
  1254. title => pid_to_cmdline($masterdb, $pid) . ': ' . $identifier);
  1255. }
  1256. done_plotting $plot;
  1257. }
  1258. BEGIN { register_generator \&generate_plot_x11_resource_count; }
  1259. sub generate_plot_x11_pixmap_size {
  1260. my $plotter = shift;
  1261. my $masterdb = shift;
  1262. my $plot = $plotter->new_linespoints(
  1263. key => '1071_x11_pixmap_size_%d',
  1264. label => 'X11 pixmaps total size per process (excluding non-changed)',
  1265. ylabel => 'MB',
  1266. multiple => {
  1267. max_plots => 2,
  1268. max_per_plot => 10,
  1269. split_f => sub { max @{shift()} },
  1270. split_factor => 5,
  1271. legend_f => sub { 'X11 PIXMAPS &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  1272. },
  1273. );
  1274. my @pids = uniq grep { defined && length } map { $_->{PID} } map {
  1275. exists $_->{'/usr/bin/xmeminfo'} ? @{$_->{'/usr/bin/xmeminfo'} // []} : undef
  1276. } @$masterdb;
  1277. foreach my $pid (@pids) {
  1278. my @pixmap_mem = map { my $entry = $_;
  1279. exists $entry->{'/usr/bin/xmeminfo'} ?
  1280. (sum map { $_->{Pixmap_mem} } grep { $_->{PID} == $pid } @{$entry->{'/usr/bin/xmeminfo'}}) :
  1281. undef
  1282. } @$masterdb;
  1283. my $identifier = join ' / ', x11_pid_to_identifier($pid, $masterdb);
  1284. $plot->push([nonzero has_changes b2mb @pixmap_mem],
  1285. title => pid_to_cmdline($masterdb, $pid) . ': ' . $identifier);
  1286. }
  1287. done_plotting $plot;
  1288. }
  1289. BEGIN { register_generator \&generate_plot_x11_pixmap_size; }
  1290. sub generate_plot_df {
  1291. my $plotter = shift;
  1292. my $masterdb = shift;
  1293. my @mountpoints = uniq map { keys %{$_->{'/bin/df'}} } @$masterdb;
  1294. #print Dumper(\@mountpoints);
  1295. my $plot = $plotter->new_linespoints(
  1296. key => '2001_diskspace',
  1297. label => '1. Disk space usage per mount point\n2. Global file descriptor usage %',
  1298. legend => 'DISK USED, GLOBAL FD %',
  1299. ylabel => 'percentage used',
  1300. );
  1301. my $maxtitle = max map { length } @mountpoints;
  1302. foreach my $mountpoint (sort @mountpoints) {
  1303. my ($filesystem) = uniq map { $_->{'/bin/df'}->{$mountpoint}->{filesystem} } @$masterdb;
  1304. $plot->push(
  1305. [nonzero map { $_->{'/bin/df'}->{$mountpoint}->{capacity} } @$masterdb],
  1306. title => $filesystem ? sprintf("%-${maxtitle}s \t[$filesystem]", $mountpoint) : $mountpoint,
  1307. );
  1308. }
  1309. $plot->push(
  1310. [nonzero map {
  1311. $_->{'/proc/sys/fs/file-nr'}->{max_fds} > 0 ?
  1312. ($_->{'/proc/sys/fs/file-nr'}->{allocated_fds} /
  1313. $_->{'/proc/sys/fs/file-nr'}->{max_fds}) * 100
  1314. : undef
  1315. } @$masterdb],
  1316. lw => 5, title => 'Global FD usage %',
  1317. );
  1318. done_plotting $plot;
  1319. }
  1320. BEGIN { register_generator \&generate_plot_df; }
  1321. sub generate_plot_fd {
  1322. my $plotter = shift;
  1323. my $masterdb = shift;
  1324. my @pids = uniq map { keys %{$_->{'/proc/pid/fd_count'}} } @$masterdb;
  1325. my $plot = $plotter->new_histogram(
  1326. key => '1080_fdcount',
  1327. label => 'File descriptors per process',
  1328. legend => 'FILE DESCRIPTORS',
  1329. ylabel => 'count',
  1330. column_limit => 1,
  1331. reduce_f => sub {
  1332. my @leftovers;
  1333. foreach my $idx (0 .. @$masterdb-1) {
  1334. push @leftovers, sum map {
  1335. exists $_->{__data} &&
  1336. exists $_->{__data}->[$idx] ?
  1337. $_->{__data}->[$idx] : undef
  1338. } @_;
  1339. }
  1340. return [nonzero @leftovers],
  1341. title => 'Sum of ' . scalar(@_) . ' process FDs';
  1342. },
  1343. );
  1344. foreach my $pid (@pids) {
  1345. $plot->push(
  1346. [nonzero map { $_->{'/proc/pid/fd_count'}->{$pid} } @$masterdb],
  1347. title => pid_to_cmdline($masterdb, $pid),
  1348. );
  1349. }
  1350. $plot->sort(sub { max @{shift()} });
  1351. $plot->reduce;
  1352. $plot->sort(\&max_change, sub { max @{shift()} });
  1353. done_plotting $plot;
  1354. }
  1355. BEGIN { register_generator \&generate_plot_fd; }
  1356. sub generate_plot_fd_changes {
  1357. my $plotter = shift;
  1358. my $masterdb = shift;
  1359. my @pids = uniq map { keys %{$_->{'/proc/pid/fd_count'}} } @$masterdb;
  1360. my $plot = $plotter->new_linespoints(
  1361. key => '1080_fdcount_changes',
  1362. label => 'File descriptors per process (excluding non-changed)',
  1363. legend => 'FILE DESCRIPTOR CHANGES',
  1364. ylabel => 'count',
  1365. );
  1366. foreach my $pid (@pids) {
  1367. $plot->push(
  1368. [nonzero has_changes map { $_->{'/proc/pid/fd_count'}->{$pid} } @$masterdb],
  1369. title => pid_to_cmdline($masterdb, $pid),
  1370. );
  1371. }
  1372. $plot->sort(sub { shift->[-1] });
  1373. done_plotting $plot;
  1374. }
  1375. BEGIN { register_generator \&generate_plot_fd_changes; }
  1376. sub generate_plot_pid_fd {
  1377. my $plotter = shift;
  1378. my $masterdb = shift;
  1379. my @pids = uniq map { keys %{$_->{'/proc/pid/fd'}} } @$masterdb;
  1380. foreach my $fdtype (keys %SP::Endurance::Parser::fdtypemap) {
  1381. my $plot = $plotter->new_histogram(
  1382. key => "1081_fdcount_$fdtype",
  1383. label => ucfirst($fdtype) . ' file descriptor use per process',
  1384. legend => 'FILE DESCRIPTORS &mdash; ' . uc $fdtype,
  1385. ylabel => 'count',
  1386. column_limit => 1,
  1387. reduce_f => sub {
  1388. my @leftovers;
  1389. foreach my $idx (0 .. @$masterdb-1) {
  1390. push @leftovers, sum map {
  1391. exists $_->{__data} &&
  1392. exists $_->{__data}->[$idx] ?
  1393. $_->{__data}->[$idx] : undef
  1394. } @_;
  1395. }
  1396. return [nonzero @leftovers],
  1397. title => 'Sum of ' . scalar(@_) . ' process FDs';
  1398. },
  1399. );
  1400. foreach my $pid (@pids) {
  1401. $plot->push(
  1402. [nonzero map {
  1403. if (exists $_->{'/proc/pid/fd'}->{$pid}) {
  1404. my @entry = split ',', $_->{'/proc/pid/fd'}->{$pid};
  1405. exists $entry[$SP::Endurance::Parser::fdtypemap{$fdtype}] ?
  1406. $entry[$SP::Endurance::Parser::fdtypemap{$fdtype}] : undef
  1407. } else { undef }
  1408. } @$masterdb],
  1409. title => pid_to_cmdline($masterdb, $pid),
  1410. );
  1411. }
  1412. $plot->sort(sub { max @{shift()} });
  1413. $plot->reduce;
  1414. $plot->sort(\&max_change, sub { max @{shift()} });
  1415. done_plotting $plot;
  1416. }
  1417. }
  1418. BEGIN { register_generator \&generate_plot_pid_fd; }
  1419. sub generate_plot_pid_fd_changes {
  1420. my $plotter = shift;
  1421. my $masterdb = shift;
  1422. my @pids = uniq map { keys %{$_->{'/proc/pid/fd'}} } @$masterdb;
  1423. foreach my $fdtype (keys %SP::Endurance::Parser::fdtypemap) {
  1424. my $plot = $plotter->new_linespoints(
  1425. key => "1081_fdcount_${fdtype}_changes",
  1426. label => ucfirst($fdtype) . ' file descriptor use per process (excluding non-changed)',
  1427. legend => 'FILE DESCRIPTOR CHANGES &mdash; ' . uc $fdtype,
  1428. ylabel => 'count',
  1429. );
  1430. foreach my $pid (@pids) {
  1431. $plot->push(
  1432. [nonzero has_changes map {
  1433. if (exists $_->{'/proc/pid/fd'}->{$pid}) {
  1434. my @entry = map { int } split ',', $_->{'/proc/pid/fd'}->{$pid};
  1435. exists $entry[$SP::Endurance::Parser::fdtypemap{$fdtype}] ?
  1436. $entry[$SP::Endurance::Parser::fdtypemap{$fdtype}] : undef
  1437. } else { undef }
  1438. } @$masterdb],
  1439. title => pid_to_cmdline($masterdb, $pid),
  1440. );
  1441. }
  1442. $plot->sort(sub { shift->[-1] });
  1443. done_plotting $plot;
  1444. }
  1445. }
  1446. BEGIN { register_generator \&generate_plot_pid_fd_changes; }
  1447. sub generate_plot_interrupts {
  1448. my $plotter = shift;
  1449. my $masterdb = shift;
  1450. my $plot = $plotter->new_linespoints(
  1451. key => '2070_interrupts',
  1452. label => 'Interrupts.',
  1453. legend => 'INTERRUPTS',
  1454. ylabel => 'count per second',
  1455. y2label => 'total count per second',
  1456. );
  1457. my @interrupts = uniq grep { defined && length } map { keys %{$_->{'/proc/interrupts'} // {}} } @$masterdb;
  1458. return unless @interrupts > 0;
  1459. foreach my $interrupt (@interrupts) {
  1460. my ($desc) = uniq grep { defined && length } map {
  1461. exists $_->{'/proc/interrupts'}->{$interrupt} &&
  1462. exists $_->{'/proc/interrupts'}->{$interrupt}->{desc} ?
  1463. $_->{'/proc/interrupts'}->{$interrupt}->{desc} : undef
  1464. } @$masterdb;
  1465. my $idx = 0;
  1466. $plot->push(
  1467. [nonzero change_per_second $masterdb,
  1468. cumulative_to_changes map {
  1469. exists $_->{'/proc/interrupts'}->{$interrupt} &&
  1470. exists $_->{'/proc/interrupts'}->{$interrupt}->{count} ?
  1471. $_->{'/proc/interrupts'}->{$interrupt}->{count} : undef
  1472. } @$masterdb],
  1473. axes => 'x1y1', title => sprintf("%-4s %s", $interrupt . ':', $desc),
  1474. );
  1475. }
  1476. my @total_interrupts = map {
  1477. my $entry = $_;
  1478. sum map { exists $_->{count} ? $_->{count} : undef } values %{$entry->{'/proc/interrupts'}}
  1479. } @$masterdb;
  1480. $plot->push(
  1481. [nonzero change_per_second $masterdb, cumulative_to_changes @total_interrupts],
  1482. lw => 5, axes => 'x2y2', title => 'Total interrupts');
  1483. $plot->sort(sub { shift->[-1] });
  1484. done_plotting $plot;
  1485. }
  1486. BEGIN { register_generator \&generate_plot_interrupts; }
  1487. sub generate_plot_diskstats_reads_mb {
  1488. my $plotter = shift;
  1489. my $masterdb = shift;
  1490. my @devices = uniq map { keys %{$_->{'/proc/diskstats'} // {}} } @$masterdb;
  1491. return unless @devices > 0;
  1492. my $plot = $plotter->new_linespoints(
  1493. key => '2100_diskstats_reads_mb',
  1494. label => 'Bytes read from device',
  1495. legend => 'DISK READS &mdash; MB',
  1496. ylabel => 'MB',
  1497. );
  1498. foreach my $device (@devices) {
  1499. $plot->push(
  1500. [b2mb nonzero cumulative_to_changes map {
  1501. exists $_->{'/proc/diskstats'}->{$device} &&
  1502. exists $_->{'/proc/diskstats'}->{$device}->{sectors_read} ?
  1503. $_->{'/proc/diskstats'}->{$device}->{sectors_read} * SECTOR_SIZE :
  1504. undef
  1505. } @$masterdb],
  1506. title => fs_to_mountpoint($device, $masterdb),
  1507. );
  1508. }
  1509. $plot->sort(sub { shift->[-1] });
  1510. done_plotting $plot;
  1511. }
  1512. BEGIN { register_generator \&generate_plot_diskstats_reads_mb; }
  1513. sub generate_plot_diskstats_reads_mb_per_second {
  1514. my $plotter = shift;
  1515. my $masterdb = shift;
  1516. my @devices = uniq map { keys %{$_->{'/proc/diskstats'} // {}} } @$masterdb;
  1517. return unless @devices > 0;
  1518. my $plot = $plotter->new_linespoints(
  1519. key => '2100_diskstats_reads_mb_per_second',
  1520. label => 'Bytes read from device per second',
  1521. legend => 'DISK READS &mdash; MB/s',
  1522. ylabel => 'MB per second',
  1523. );
  1524. foreach my $device (@devices) {
  1525. $plot->push(
  1526. [b2mb nonzero change_per_second $masterdb, cumulative_to_changes map {
  1527. exists $_->{'/proc/diskstats'}->{$device} &&
  1528. exists $_->{'/proc/diskstats'}->{$device}->{sectors_read} ?
  1529. $_->{'/proc/diskstats'}->{$device}->{sectors_read} * SECTOR_SIZE :
  1530. undef
  1531. } @$masterdb],
  1532. title => fs_to_mountpoint($device, $masterdb),
  1533. );
  1534. }
  1535. $plot->sort(sub { shift->[-1] });
  1536. done_plotting $plot;
  1537. }
  1538. BEGIN { register_generator \&generate_plot_diskstats_reads_mb_per_second; }
  1539. sub generate_plot_diskstats_written_mb {
  1540. my $plotter = shift;
  1541. my $masterdb = shift;
  1542. my @devices = uniq map { keys %{$_->{'/proc/diskstats'} // {}} } @$masterdb;
  1543. return unless @devices > 0;
  1544. my $plot = $plotter->new_linespoints(
  1545. key => '2100_diskstats_written_mb',
  1546. label => 'Bytes written to device',
  1547. legend => 'DISK WRITES &mdash; MB',
  1548. ylabel => 'MB',
  1549. );
  1550. foreach my $device (@devices) {
  1551. $plot->push(
  1552. [b2mb nonzero cumulative_to_changes map {
  1553. exists $_->{'/proc/diskstats'}->{$device} &&
  1554. exists $_->{'/proc/diskstats'}->{$device}->{sectors_written} ?
  1555. $_->{'/proc/diskstats'}->{$device}->{sectors_written} * SECTOR_SIZE :
  1556. undef
  1557. } @$masterdb],
  1558. title => fs_to_mountpoint($device, $masterdb),
  1559. );
  1560. }
  1561. $plot->sort(sub { shift->[-1] });
  1562. done_plotting $plot;
  1563. }
  1564. BEGIN { register_generator \&generate_plot_diskstats_written_mb; }
  1565. sub generate_plot_diskstats_written_mb_per_second {
  1566. my $plotter = shift;
  1567. my $masterdb = shift;
  1568. my @devices = uniq map { keys %{$_->{'/proc/diskstats'} // {}} } @$masterdb;
  1569. return unless @devices > 0;
  1570. my $plot = $plotter->new_linespoints(
  1571. key => '2100_diskstats_written_mb_per_second',
  1572. label => 'Bytes written to device per second',
  1573. legend => 'DISK WRITES &mdash; MB/s',
  1574. ylabel => 'MB per second',
  1575. );
  1576. foreach my $device (@devices) {
  1577. $plot->push(
  1578. [b2mb nonzero change_per_second $masterdb, cumulative_to_changes map {
  1579. exists $_->{'/proc/diskstats'}->{$device} &&
  1580. exists $_->{'/proc/diskstats'}->{$device}->{sectors_written} ?
  1581. $_->{'/proc/diskstats'}->{$device}->{sectors_written} * SECTOR_SIZE :
  1582. undef
  1583. } @$masterdb],
  1584. title => fs_to_mountpoint($device, $masterdb),
  1585. );
  1586. }
  1587. $plot->sort(sub { shift->[-1] });
  1588. done_plotting $plot;
  1589. }
  1590. BEGIN { register_generator \&generate_plot_diskstats_written_mb_per_second; }
  1591. sub generate_plot_bmestat {
  1592. my $plotter = shift;
  1593. my $masterdb = shift;
  1594. my ($type) = uniq map { $_->{'/usr/bin/bmestat'}->{battery_type} } @$masterdb;
  1595. $type = " (type: $type)" if length $type;
  1596. my $plot = $plotter->new_linespoints(
  1597. key => '2000_bmestat',
  1598. label => "Battery$type\\n Left: charge %, temperature\\n Right: voltage",
  1599. legend => 'BATTERY',
  1600. ylabel => 'charge %, temperature in celsius',
  1601. y2label => 'V',
  1602. );
  1603. $plot->push(
  1604. [nonzero map { $_->{'/usr/bin/bmestat'}->{battery_pct_level} } @$masterdb],
  1605. axes => 'x1y1', lw => 5, title => 'Charge % left',
  1606. );
  1607. $plot->push(
  1608. [nonzero map { $_->{'/usr/bin/bmestat'}->{battery_temperature} } @$masterdb],
  1609. axes => 'x1y1', title => 'Temperature',
  1610. );
  1611. $plot->push(
  1612. [nonzero map { $_->{'/usr/bin/bmestat'}->{battery_cur_voltage} / 1_000 } @$masterdb],
  1613. axes => 'x2y2', title => 'Voltage',
  1614. );
  1615. if ($plot->count) {
  1616. my @backlights = uniq map { keys %{$_->{'/sys/class/backlight'}} } @$masterdb;
  1617. foreach my $bldev (@backlights) {
  1618. $plot->push(
  1619. [nonzero map {
  1620. exists $_->{'/sys/class/backlight'}->{$bldev} ?
  1621. ($_->{'/sys/class/backlight'}->{$bldev}->{actual_brightness} /
  1622. $_->{'/sys/class/backlight'}->{$bldev}->{max_brightness}) * 100
  1623. : undef
  1624. } @$masterdb],
  1625. axes => 'x1y1', title => "Backlight $bldev brightness %",
  1626. );
  1627. }
  1628. }
  1629. done_plotting $plot;
  1630. }
  1631. BEGIN { register_generator \&generate_plot_bmestat; }
  1632. sub generate_plot_ramzswap_1 {
  1633. my $plotter = shift;
  1634. my $masterdb = shift;
  1635. my $plot = $plotter->new_linespoints(
  1636. key => '2111_ramzswap_1',
  1637. label => 'ramzswap (compressed swap) reads and writes',
  1638. legend => 'COMPR-SWAP READS/WRITES',
  1639. ylabel => 'MB',
  1640. );
  1641. foreach my $key (qw/NumReads NumWrites BDevNumReads BDevNumWrites/) {
  1642. my @counts = map {
  1643. exists $_->{ramzswap} &&
  1644. exists $_->{ramzswap}->{'/dev/ramzswap0'} &&
  1645. exists $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} ?
  1646. $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} : undef
  1647. } @$masterdb;
  1648. # Convert "reads" and "writes" to megabytes. I'm assuming that they are
  1649. # always page sized operations...
  1650. @counts = map { $_ * PAGE_SIZE } @counts;
  1651. $plot->push([nonzero cumulative_to_changes b2mb @counts],
  1652. title => $key);
  1653. }
  1654. done_plotting $plot;
  1655. }
  1656. BEGIN { register_generator \&generate_plot_ramzswap_1; }
  1657. sub generate_plot_ramzswap_2 {
  1658. my $plotter = shift;
  1659. my $masterdb = shift;
  1660. my $plot = $plotter->new_linespoints(
  1661. key => '2111_ramzswap_2',
  1662. label => 'ramzswap (compressed swap) memory usage',
  1663. legend => 'COMPR-SWAP MEM USAGE',
  1664. ylabel => 'GoodCompress and NoCompress %',
  1665. y2label => 'MB',
  1666. );
  1667. foreach my $key (qw/OrigDataSize ComprDataSize MemUsedTotal/) {
  1668. $plot->push(
  1669. [nonzero kb2mb map {
  1670. exists $_->{ramzswap} &&
  1671. exists $_->{ramzswap}->{'/dev/ramzswap0'} &&
  1672. exists $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} ?
  1673. $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} : undef
  1674. } @$masterdb],
  1675. lw => 5, axes => 'x2y2', title => $key,
  1676. );
  1677. }
  1678. $plot->push(
  1679. [nonzero b2mb map { $_ * PAGE_SIZE } map {
  1680. exists $_->{ramzswap} &&
  1681. exists $_->{ramzswap}->{'/dev/ramzswap0'} &&
  1682. exists $_->{ramzswap}->{'/dev/ramzswap0'}->{ZeroPages} ?
  1683. $_->{ramzswap}->{'/dev/ramzswap0'}->{ZeroPages} : undef
  1684. } @$masterdb],
  1685. lw => 5, axes => 'x2y2', title => 'ZeroPages',
  1686. );
  1687. foreach my $key (qw/GoodCompress NoCompress/) {
  1688. $plot->push(
  1689. [nonzero map {
  1690. exists $_->{ramzswap} &&
  1691. exists $_->{ramzswap}->{'/dev/ramzswap0'} &&
  1692. exists $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} ?
  1693. $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} : undef
  1694. } @$masterdb],
  1695. axes => 'x1y1', title => $key,
  1696. );
  1697. }
  1698. done_plotting $plot;
  1699. }
  1700. BEGIN { register_generator \&generate_plot_ramzswap_2; }
  1701. sub generate_plot_ramzswap_3 {
  1702. my $plotter = shift;
  1703. my $masterdb = shift;
  1704. my $plot = $plotter->new_linespoints(
  1705. key => '2111_ramzswap_3',
  1706. label => 'ramzswap (compressed swap) errors',
  1707. legend => 'COMPR-SWAP ERRORS',
  1708. ylabel => 'count',
  1709. );
  1710. foreach my $key (qw/FailedReads FailedWrites InvalidIO/) {
  1711. $plot->push(
  1712. [nonzero cumulative_to_changes map {
  1713. exists $_->{ramzswap} &&
  1714. exists $_->{ramzswap}->{'/dev/ramzswap0'} &&
  1715. exists $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} ?
  1716. $_->{ramzswap}->{'/dev/ramzswap0'}->{$key} : undef
  1717. } @$masterdb],
  1718. title => $key,
  1719. );
  1720. }
  1721. done_plotting $plot;
  1722. }
  1723. BEGIN { register_generator \&generate_plot_ramzswap_3; }
  1724. sub generate_plot_cgroups {
  1725. my $plotter = shift;
  1726. my $masterdb = shift;
  1727. my @cgroups = uniq grep { defined && length } map { keys %{$_->{cgroups} // {}} } @$masterdb;
  1728. return unless @cgroups > 0;
  1729. my $tmp = 0;
  1730. my %cgroup_colors = map { $_ => $SP::Endurance::Plot::line_colors[$tmp++ % scalar @SP::Endurance::Plot::line_colors] } @cgroups;
  1731. foreach my $key (qw/memory.usage_in_bytes memory.memsw.usage_in_bytes/) {
  1732. my $plot;
  1733. $plot = $plotter->new_linespoints(
  1734. key => '2300_cgroups-memory',
  1735. label => 'Memory usage per cgroup.',
  1736. legend => 'CGROUPS MEMORY',
  1737. ylabel => 'MB',
  1738. ) if $key eq 'memory.usage_in_bytes';
  1739. $plot = $plotter->new_linespoints(
  1740. key => '2301_cgroups-memsw',
  1741. label => 'Memory+Swap usage per cgroup.',
  1742. legend => 'CGROUPS MEMORY+SWAP',
  1743. ylabel => 'MB',
  1744. ) if $key eq 'memory.memsw.usage_in_bytes';
  1745. foreach my $cgroup (sort @cgroups) {
  1746. $plot->push([b2mb nonzero map {
  1747. exists $_->{cgroups} &&
  1748. exists $_->{cgroups}->{$cgroup} &&
  1749. exists $_->{cgroups}->{$cgroup}->{$key} ?
  1750. $_->{cgroups}->{$cgroup}->{$key} :
  1751. undef
  1752. } @$masterdb],
  1753. lc => $cgroup_colors{$cgroup},
  1754. title => $cgroup,
  1755. );
  1756. }
  1757. my $limit_key = $key;
  1758. $limit_key =~ s/usage/limit/;
  1759. foreach my $cgroup (sort @cgroups) {
  1760. $plot->push([b2mb nonzero map {
  1761. exists $_->{cgroups} &&
  1762. exists $_->{cgroups}->{$cgroup} &&
  1763. exists $_->{cgroups}->{$cgroup}->{$limit_key} &&
  1764. $_->{cgroups}->{$cgroup}->{$limit_key} != CGROUP_UNLIMITED ?
  1765. $_->{cgroups}->{$cgroup}->{$limit_key} : undef
  1766. } @$masterdb],
  1767. lc => $cgroup_colors{$cgroup}, lw => 5,
  1768. title => 'Limit for: ' . $cgroup,
  1769. );
  1770. }
  1771. done_plotting $plot;
  1772. }
  1773. foreach my $memory_stat (qw/cache rss mapped_file swap inactive_anon
  1774. active_anon inactive_file active_file unevictable
  1775. pgpgin pgpgout/) {
  1776. my $plot = $plotter->new_linespoints(
  1777. key => "2302_cgroups-$memory_stat",
  1778. label => {
  1779. cache => 'Page cache per cgroup.',
  1780. rss => 'RSS (anonymous + swap cache) per cgroup.',
  1781. mapped_file => 'Mapped file per cgroup.',
  1782. swap => 'Swap usage per cgroup.',
  1783. inactive_anon => 'Anon + swap cache on inactive LRU list per cgroup.',
  1784. active_anon => 'Anon + swap cache on active LRU list per cgroup.',
  1785. inactive_file => 'File-backed memory on inactive LRU list per cgroup.',
  1786. active_file => 'File-backed memory on active LRU list per cgroup.',
  1787. unevictable => 'Unevictable memory per cgroup.',
  1788. pgpgin => 'Number of charging events to the memory cgroup.\n' .
  1789. '(Charging event = page accounted as mapped anon page or cache page.)',
  1790. pgpgout => 'Number of uncharging events to the memory cgroup.\n' .
  1791. '(Uncharging event = page unaccounted from the cgroup.)',
  1792. }->{$memory_stat},
  1793. legend => 'CGROUPS ' . uc $memory_stat,
  1794. ylabel => ($memory_stat eq 'pgpgin' or $memory_stat eq 'pgpgout') ?
  1795. 'count' : 'MB',
  1796. );
  1797. foreach my $cgroup (sort @cgroups) {
  1798. my @dataset = map {
  1799. exists $_->{cgroups} &&
  1800. exists $_->{cgroups}->{$cgroup} &&
  1801. exists $_->{cgroups}->{$cgroup}->{'memory.stat'} &&
  1802. exists $_->{cgroups}->{$cgroup}->{'memory.stat'}->{$memory_stat} ?
  1803. $_->{cgroups}->{$cgroup}->{'memory.stat'}->{$memory_stat} :
  1804. undef
  1805. } @$masterdb;
  1806. if ($memory_stat eq 'pgpgin' or $memory_stat eq 'pgpgout') {
  1807. $plot->push([b2mb nonzero cumulative_to_changes @dataset], title => $cgroup);
  1808. } else {
  1809. $plot->push([b2mb nonzero @dataset], title => $cgroup);
  1810. }
  1811. }
  1812. done_plotting $plot;
  1813. }
  1814. foreach my $key (qw/cgroup.procs tasks/) {
  1815. my $plot = $plotter->new_linespoints(
  1816. key => { 'cgroup.procs' => '2305_cgroups-procs',
  1817. tasks => '2305_cgroups-tasks' }->{$key},
  1818. label => { 'cgroup.procs' => 'Process count per cgroup.',
  1819. tasks => 'Task count per cgroup.' }->{$key},
  1820. legend => { 'cgroup.procs' => 'CGROUPS PROCESS COUNT',
  1821. tasks => 'CGROUPS TASK COUNT' }->{$key},
  1822. ylabel => 'count',
  1823. );
  1824. foreach my $cgroup (sort @cgroups) {
  1825. $plot->push([nonzero map {
  1826. exists $_->{cgroups} &&
  1827. exists $_->{cgroups}->{$cgroup} &&
  1828. exists $_->{cgroups}->{$cgroup}->{$key} ?
  1829. $_->{cgroups}->{$cgroup}->{$key} :
  1830. undef
  1831. } @$masterdb],
  1832. title => $cgroup,
  1833. );
  1834. }
  1835. done_plotting $plot;
  1836. }
  1837. foreach my $key (qw/memory.failcnt memory.memsw.failcnt/) {
  1838. my $plot = $plotter->new_linespoints(
  1839. key => { 'memory.failcnt' => '2306_cgroups-memory_failcnt',
  1840. 'memory.memsw.failcnt' => '2306_cgroups-memsw_failcnt' }->{$key},
  1841. label => { 'memory.failcnt' => 'Memory fail count per cgroup.',
  1842. 'memory.memsw.failcnt' => 'Memory+Swap fail count per cgroup.' }->{$key},
  1843. legend => { 'memory.failcnt' => 'CGROUPS MEMORY FAIL COUNT',
  1844. 'memory.memsw.failcnt' => 'CGROUPS MEMORY+SWAP FAIL COUNT' }->{$key},
  1845. ylabel => 'count',
  1846. );
  1847. foreach my $cgroup (sort @cgroups) {
  1848. $plot->push([nonzero cumulative_to_changes map {
  1849. exists $_->{cgroups} &&
  1850. exists $_->{cgroups}->{$cgroup} &&
  1851. exists $_->{cgroups}->{$cgroup}->{$key} ?
  1852. $_->{cgroups}->{$cgroup}->{$key} :
  1853. undef
  1854. } @$masterdb],
  1855. title => $cgroup,
  1856. );
  1857. }
  1858. done_plotting $plot;
  1859. }
  1860. }
  1861. BEGIN { register_generator \&generate_plot_cgroups; }
  1862. sub generate_plot_networking {
  1863. my $plotter = shift;
  1864. my $masterdb = shift;
  1865. my @interfaces = uniq map { keys %{$_->{'/sbin/ifconfig'} // {}} } @$masterdb;
  1866. foreach my $interface (sort @interfaces) {
  1867. foreach my $key (qw/bytes packets/) {
  1868. my $plot = $plotter->new_linespoints(
  1869. key => "2090_networking-$key-$interface",
  1870. label => "RX and TX $key for networking interface $interface",
  1871. legend => 'NET RX/TX ' . uc($key) . ' &mdash; ' . $interface,
  1872. ylabel => { bytes => 'kB', packets => 'packets' }->{$key},
  1873. );
  1874. foreach my $rxtx (qw/RX TX/) {
  1875. my @dataset = cumulative_to_changes map {
  1876. exists $_->{'/sbin/ifconfig'}->{$interface} &&
  1877. exists $_->{'/sbin/ifconfig'}->{$interface}->{$rxtx} &&
  1878. exists $_->{'/sbin/ifconfig'}->{$interface}->{$rxtx}->{$key} ?
  1879. $_->{'/sbin/ifconfig'}->{$interface}->{$rxtx}->{$key} :
  1880. undef
  1881. } @$masterdb;
  1882. if ($key eq 'bytes') {
  1883. foreach (@dataset) {
  1884. $_ /= 1_000 if defined;
  1885. }
  1886. }
  1887. $plot->push([nonzero @dataset], title => $rxtx . ': ' . $interface);
  1888. }
  1889. done_plotting $plot;
  1890. }
  1891. }
  1892. }
  1893. BEGIN { register_generator \&generate_plot_networking; }
  1894. sub generate_plot_pagetypeinfo {
  1895. my $plotter = shift;
  1896. my $masterdb = shift;
  1897. my @pagetypes = uniq map { keys %{$_->{'/proc/pagetypeinfo'}->{0}->{Normal}} } @{$masterdb};
  1898. foreach my $pagetype (@pagetypes) {
  1899. my $ok = 0;
  1900. foreach my $ordernum (0 .. 10) {
  1901. foreach my $entry (map { $_->{'/proc/pagetypeinfo'}->{0}->{Normal}->{$pagetype} } @{$masterdb}) {
  1902. if (any { $_ } @$entry) {
  1903. $ok = 1;
  1904. last;
  1905. }
  1906. }
  1907. }
  1908. next unless $ok;
  1909. my $plot = $plotter->new_histogram(
  1910. key => '2274_pagetypeinfo_' . lc $pagetype,
  1911. label => "Free memory in $pagetype migrate type block pool (from /proc/pagetypeinfo)",
  1912. legend => 'PAGETYPE &mdash; ' . uc $pagetype,
  1913. ylabel => 'MB',
  1914. );
  1915. foreach my $ordernum (reverse (0 .. 10)) {
  1916. my @values;
  1917. foreach my $entry (map { $_->{'/proc/pagetypeinfo'}->{0}->{Normal}->{$pagetype} } @{$masterdb}) {
  1918. push @values, $entry->[$ordernum] * (1 << $ordernum) * PAGE_SIZE
  1919. }
  1920. $plot->push([b2mb @values],
  1921. title => ucfirst($pagetype) . " order 2^$ordernum",
  1922. );
  1923. }
  1924. done_plotting $plot;
  1925. }
  1926. }
  1927. BEGIN { register_generator \&generate_plot_pagetypeinfo; }
  1928. sub generate_plot_process_state_count {
  1929. my $plotter = shift;
  1930. my $masterdb = shift;
  1931. my $plot = $plotter->new_histogram(
  1932. key => '2065_nonsleeping_process_count',
  1933. label => 'Number of processes in non-sleep states.',
  1934. legend => 'NON-SLEEP PROCESS COUNT',
  1935. ylabel => 'count',
  1936. );
  1937. my %states;
  1938. foreach my $entry (@$masterdb) {
  1939. next unless exists $entry->{'/proc/pid/stat'};
  1940. foreach (values %{$entry->{'/proc/pid/stat'}}) {
  1941. my %stat = split ',';
  1942. next unless exists $stat{state};
  1943. $states{$stat{state}} = 1;
  1944. }
  1945. }
  1946. foreach my $state (keys %states) {
  1947. my @count;
  1948. foreach my $entry (@$masterdb) {
  1949. my $cnt = grep { $_ eq $state } map {
  1950. my %stat = split ',';
  1951. exists $stat{state} ? $stat{state} : undef
  1952. } values %{$entry->{'/proc/pid/stat'} // {}};
  1953. push @count, $cnt;
  1954. }
  1955. $plot->push([nonzero @count],
  1956. title => $state . {
  1957. D => ' (Uninterruptible disk sleep)',
  1958. R => ' (Running)',
  1959. T => ' (Traced or stopped)',
  1960. W => ' (Paging)',
  1961. Z => ' (Zombie)',
  1962. }->{$state},
  1963. );
  1964. }
  1965. $plot->sort(sub { shift->[-1] });
  1966. done_plotting $plot;
  1967. }
  1968. BEGIN { register_generator \&generate_plot_process_state_count; }
  1969. my %wchan_suffix = (0 => ' (in user space)');
  1970. sub generate_plot_wchan_count {
  1971. my $plotter = shift;
  1972. my $masterdb = shift;
  1973. my $plot = $plotter->new_histogram(
  1974. key => '2350_wchan-count',
  1975. label => 'Process count per wait channel',
  1976. legend => 'WAIT CHANNEL COUNT',
  1977. ylabel => 'process count',
  1978. );
  1979. my @wchans = uniq map { keys %{$_->{'/proc/pid/wchan'}} } @$masterdb;
  1980. foreach my $wchan (@wchans) {
  1981. $plot->push([nonzero map {
  1982. exists $_->{'/proc/pid/wchan'} &&
  1983. exists $_->{'/proc/pid/wchan'}->{$wchan} ?
  1984. $_->{'/proc/pid/wchan'}->{$wchan} : undef
  1985. } @$masterdb],
  1986. title => $wchan . $wchan_suffix{$wchan},
  1987. );
  1988. }
  1989. $plot->sort(sub { shift->[-1] });
  1990. done_plotting $plot;
  1991. }
  1992. BEGIN { register_generator \&generate_plot_wchan_count; }
  1993. sub generate_plot_wchan_changes {
  1994. my $plotter = shift;
  1995. my $masterdb = shift;
  1996. my $plot = $plotter->new_linespoints(
  1997. key => '2351_wchan-changes',
  1998. label => 'Process count per wait channel (only changed shown)',
  1999. legend => 'WAIT CHANNEL CHANGES',
  2000. ylabel => 'process count',
  2001. );
  2002. my @wchans = uniq map { keys %{$_->{'/proc/pid/wchan'}} } @$masterdb;
  2003. foreach my $wchan (@wchans) {
  2004. $plot->push([has_changes nonzero map {
  2005. exists $_->{'/proc/pid/wchan'} &&
  2006. exists $_->{'/proc/pid/wchan'}->{$wchan} ?
  2007. $_->{'/proc/pid/wchan'}->{$wchan} : undef
  2008. } @$masterdb],
  2009. title => $wchan . $wchan_suffix{$wchan},
  2010. );
  2011. }
  2012. $plot->sort(sub { shift->[-1] });
  2013. done_plotting $plot;
  2014. }
  2015. BEGIN { register_generator \&generate_plot_wchan_changes; }
  2016. sub generate_plot_power_supply {
  2017. my $plotter = shift;
  2018. my $masterdb = shift;
  2019. my @power_supplies = uniq map { keys %{$_->{'/sys/class/power_supply'}} } @$masterdb;
  2020. my @backlights = uniq map { keys %{$_->{'/sys/class/backlight'}} } @$masterdb;
  2021. foreach my $dev (@power_supplies) {
  2022. my ($type) = uniq map { $_->{'/sys/class/power_supply'}->{$dev}->{type} } @$masterdb;
  2023. $type = '\nType: ' . $type if length $type;
  2024. my ($technology) = uniq map { $_->{'/sys/class/power_supply'}->{$dev}->{technology} } @$masterdb;
  2025. $technology = '\nTechnology: ' . $technology if length $technology;
  2026. my ($model_name) = uniq map { $_->{'/sys/class/power_supply'}->{$dev}->{model_name} } @$masterdb;
  2027. $model_name = '\nModel: ' . $model_name if length $model_name;
  2028. my ($manufacturer) = uniq map { $_->{'/sys/class/power_supply'}->{$dev}->{manufacturer} } @$masterdb;
  2029. $manufacturer = '\nManufacturer: ' . $manufacturer if length $manufacturer;
  2030. my $plot = $plotter->new_linespoints(
  2031. key => "2000_power_supply-$dev",
  2032. label => "Power supply: ${dev}${type}${technology}${model_name}${manufacturer}",
  2033. legend => "POWER SUPPLY &mdash; $dev",
  2034. ylabel => 'charge-percent, temp-C',
  2035. y2label => 'V',
  2036. );
  2037. $plot->push(
  2038. [nonzero map { $_->{'/sys/class/power_supply'}->{$dev}->{capacity} } @$masterdb],
  2039. axes => 'x1y1', lw => 5, title => 'Charge % left',
  2040. );
  2041. $plot->push(
  2042. [nonzero map { $_->{'/sys/class/power_supply'}->{$dev}->{temp} } @$masterdb],
  2043. axes => 'x1y1', title => 'Temperature',
  2044. );
  2045. $plot->push(
  2046. [nonzero map { $_->{'/sys/class/power_supply'}->{$dev}->{voltage_now} / 1e6 } @$masterdb],
  2047. axes => 'x2y2', title => 'Voltage',
  2048. );
  2049. if ($plot->count) {
  2050. foreach my $bldev (@backlights) {
  2051. $plot->push(
  2052. [nonzero map {
  2053. exists $_->{'/sys/class/backlight'}->{$bldev} ?
  2054. ($_->{'/sys/class/backlight'}->{$bldev}->{actual_brightness} /
  2055. $_->{'/sys/class/backlight'}->{$bldev}->{max_brightness}) * 100 :
  2056. undef
  2057. } @$masterdb],
  2058. axes => 'x1y1', title => "Backlight $bldev brightness %",
  2059. );
  2060. }
  2061. }
  2062. done_plotting $plot;
  2063. }
  2064. }
  2065. BEGIN { register_generator \&generate_plot_power_supply; }
  2066. sub proc_pid_io_collect_data {
  2067. my $plot = shift;
  2068. my $masterdb = shift;
  2069. my $pids = shift;
  2070. my $key = shift;
  2071. my $idx = {
  2072. read_bytes => 4,
  2073. write_bytes => 5,
  2074. cancelled_write_bytes => 6,
  2075. }->{$key};
  2076. die "Invalid $key" unless defined $idx;
  2077. foreach my $pid (@$pids) {
  2078. $plot->push(
  2079. [nonzero cumulative_to_changes b2mb map {
  2080. if (exists $_->{'/proc/pid/io'}->{$pid}) {
  2081. my @entry = unpack "d*", $_->{'/proc/pid/io'}->{$pid};
  2082. defined $entry[$idx] ? $entry[$idx] : undef
  2083. } else { undef }
  2084. } @$masterdb],
  2085. title => pid_to_cmdline($masterdb, $pid),
  2086. );
  2087. }
  2088. return $plot;
  2089. }
  2090. sub generate_plot_pid_io {
  2091. my $plotter = shift;
  2092. my $masterdb = shift;
  2093. my @pids = uniq map { keys %{$_->{'/proc/pid/io'}} } @$masterdb;
  2094. # Not generating the line graphs for 'cancelled_write_bytes' on purpose,
  2095. # the histogram is enough for now.
  2096. foreach my $key (qw/read_bytes write_bytes/) {
  2097. my $plot = $plotter->new_linespoints(
  2098. key => {
  2099. read_bytes => '1300_pid_io_read_bytes_%d',
  2100. write_bytes => '1301_pid_io_write_bytes_%d',
  2101. cancelled_write_bytes => '1302_pid_io_cancelled_write_bytes_%d',
  2102. }->{$key},
  2103. label => {
  2104. read_bytes => 'Per process disk reads.',
  2105. write_bytes => 'Per process disk writes.',
  2106. cancelled_write_bytes => 'Per process cancelled disk writes.',
  2107. }->{$key},
  2108. ylabel => 'MB',
  2109. multiple => {
  2110. max_plots => 3,
  2111. max_per_plot => 10,
  2112. split_f => sub { max @{shift()} },
  2113. split_factor => 5,
  2114. legend_f => sub {
  2115. { read_bytes => 'DISK READS',
  2116. write_bytes => 'DISK WRITES',
  2117. cancelled_write_bytes => 'CANCELLED DISK WRITES',
  2118. }->{$key} .
  2119. ' &mdash; MAX ' . ceil(max @{shift()}) . 'MB' },
  2120. },
  2121. );
  2122. proc_pid_io_collect_data $plot, $masterdb, \@pids, $key;
  2123. done_plotting $plot;
  2124. }
  2125. }
  2126. BEGIN { register_generator \&generate_plot_pid_io; }
  2127. sub generate_plot_pid_io_histogram {
  2128. my $plotter = shift;
  2129. my $masterdb = shift;
  2130. my @pids = uniq map { keys %{$_->{'/proc/pid/io'}} } @$masterdb;
  2131. foreach my $key (qw/read_bytes write_bytes cancelled_write_bytes/) {
  2132. my $plot = $plotter->new_histogram(
  2133. key => {
  2134. read_bytes => '1300_pid_io_read_bytes',
  2135. write_bytes => '1301_pid_io_write_bytes',
  2136. cancelled_write_bytes => '1302_pid_io_cancelled_write_bytes',
  2137. }->{$key},
  2138. label => {
  2139. read_bytes => 'Per process disk reads.',
  2140. write_bytes => 'Per process disk writes.',
  2141. cancelled_write_bytes => 'Per process cancelled disk writes.',
  2142. }->{$key},
  2143. legend => {
  2144. read_bytes => 'DISK READS',
  2145. write_bytes => 'DISK WRITES',
  2146. cancelled_write_bytes => 'CANCELLED DISK WRITES',
  2147. }->{$key},
  2148. ylabel => 'MB',
  2149. );
  2150. proc_pid_io_collect_data $plot, $masterdb, \@pids, $key;
  2151. $plot->sort(\&max_change, sub { max @{shift()} });
  2152. done_plotting $plot;
  2153. }
  2154. }
  2155. BEGIN { register_generator \&generate_plot_pid_io_histogram; }
  2156. sub generate_plot_upstart_jobs_respawned {
  2157. my $plotter = shift;
  2158. my $masterdb = shift;
  2159. my @jobs = uniq grep { defined and length } map { keys %{$_->{upstart_jobs_respawned}} } @$masterdb;
  2160. my $plot = $plotter->new_histogram(
  2161. key => '1400_upstart_jobs_respawned',
  2162. label => 'Jobs respawned by Upstart',
  2163. legend => 'UPSTART JOBS RESPAWNED',
  2164. ylabel => 'count',
  2165. );
  2166. foreach my $job (@jobs) {
  2167. $plot->push(
  2168. [nonzero cumulative_to_changes map {
  2169. exists $_->{upstart_jobs_respawned} &&
  2170. exists $_->{upstart_jobs_respawned}->{$job} ?
  2171. $_->{upstart_jobs_respawned}->{$job} : undef
  2172. } @$masterdb],
  2173. title => $job,
  2174. );
  2175. }
  2176. $plot->sort(\&max_change, sub { max @{shift()} });
  2177. done_plotting $plot;
  2178. }
  2179. BEGIN { register_generator \&generate_plot_upstart_jobs_respawned; }
  2180. sub generate_plot_sched_wakeups {
  2181. my $plotter = shift;
  2182. my $masterdb = shift;
  2183. my $plot = $plotter->new_linespoints(
  2184. key => '1500_sched_wakeups_%d',
  2185. label => 'Number of times the process was woken up.',
  2186. ylabel => 'count',
  2187. multiple => {
  2188. max_plots => 3,
  2189. max_per_plot => 10,
  2190. split_f => sub { max @{shift()} },
  2191. split_factor => 5,
  2192. legend_f => sub { 'SCHED &mdash; WAKEUPS &mdash; MAX ' . ceil(max @{shift()}) },
  2193. },
  2194. );
  2195. my @pids = uniq grep { defined and length } map { keys %{$_->{'/proc/pid/sched'}} } @$masterdb;
  2196. foreach my $pid (@pids) {
  2197. $plot->push(
  2198. [nonzero cumulative_to_changes map {
  2199. if (exists $_->{'/proc/pid/sched'} && exists $_->{'/proc/pid/sched'}->{$pid}) {
  2200. my @entry = unpack('d*', $_->{'/proc/pid/sched'}->{$pid});
  2201. exists $entry[$SP::Endurance::Parser::schedmap{'se.statistics.nr_wakeups'}] ?
  2202. $entry[$SP::Endurance::Parser::schedmap{'se.statistics.nr_wakeups'}] :
  2203. undef
  2204. } else { undef }
  2205. } @$masterdb],
  2206. title => pid_to_cmdline($masterdb, $pid),
  2207. );
  2208. }
  2209. done_plotting $plot;
  2210. }
  2211. BEGIN { register_generator \&generate_plot_sched_wakeups; }
  2212. sub generate_plot_sched_iowait {
  2213. my $plotter = shift;
  2214. my $masterdb = shift;
  2215. my $plot = $plotter->new_linespoints(
  2216. key => '1501_sched_iowait_%d',
  2217. label => 'Time spent waiting for I/O.',
  2218. ylabel => 'ms',
  2219. multiple => {
  2220. max_plots => 3,
  2221. max_per_plot => 10,
  2222. split_f => sub { max @{shift()} },
  2223. split_factor => 5,
  2224. legend_f => sub { 'SCHED &mdash; I/O WAIT &mdash; MAX ' . ceil(max @{shift()}) . 'MS'},
  2225. },
  2226. );
  2227. my @pids = uniq grep { defined and length } map { keys %{$_->{'/proc/pid/sched'}} } @$masterdb;
  2228. foreach my $pid (@pids) {
  2229. $plot->push(
  2230. [nonzero cumulative_to_changes map {
  2231. if (exists $_->{'/proc/pid/sched'} && exists $_->{'/proc/pid/sched'}->{$pid}) {
  2232. my @entry = unpack('d*', $_->{'/proc/pid/sched'}->{$pid});
  2233. exists $entry[$SP::Endurance::Parser::schedmap{'se.statistics.iowait_sum'}] ?
  2234. $entry[$SP::Endurance::Parser::schedmap{'se.statistics.iowait_sum'}] :
  2235. undef
  2236. } else { undef }
  2237. } @$masterdb],
  2238. title => pid_to_cmdline($masterdb, $pid),
  2239. );
  2240. }
  2241. done_plotting $plot;
  2242. }
  2243. BEGIN { register_generator \&generate_plot_sched_iowait; }
  2244. sub generate_plot_sched_block_max {
  2245. my $plotter = shift;
  2246. my $masterdb = shift;
  2247. my $plot = $plotter->new_linespoints(
  2248. key => '1502_sched_block_max_%d',
  2249. label => 'Maximum time the process has been blocked in uninterruptible sleep.',
  2250. ylabel => 'ms',
  2251. multiple => {
  2252. max_plots => 3,
  2253. max_per_plot => 10,
  2254. split_f => sub { max @{shift()} },
  2255. split_factor => 5,
  2256. legend_f => sub { 'SCHED &mdash; BLOCK_MAX &mdash; ' . ceil(max @{shift()}) . 'MS'},
  2257. },
  2258. );
  2259. my @pids = uniq grep { defined and length } map { keys %{$_->{'/proc/pid/sched'}} } @$masterdb;
  2260. foreach my $pid (@pids) {
  2261. $plot->push(
  2262. [nonzero map {
  2263. if (exists $_->{'/proc/pid/sched'} && exists $_->{'/proc/pid/sched'}->{$pid}) {
  2264. my @entry = unpack('d*', $_->{'/proc/pid/sched'}->{$pid});
  2265. exists $entry[$SP::Endurance::Parser::schedmap{'se.statistics.block_max'}] ?
  2266. $entry[$SP::Endurance::Parser::schedmap{'se.statistics.block_max'}] :
  2267. undef
  2268. } else { undef }
  2269. } @$masterdb],
  2270. title => pid_to_cmdline($masterdb, $pid),
  2271. );
  2272. }
  2273. done_plotting $plot;
  2274. }
  2275. BEGIN { register_generator \&generate_plot_sched_block_max; }
  2276. sub generate_plot_sched_wait_max {
  2277. my $plotter = shift;
  2278. my $masterdb = shift;
  2279. my $plot = $plotter->new_linespoints(
  2280. key => '1503_sched_wait_max_%d',
  2281. label => 'Maximum time the process waited in kernel runqueue before entering CPU.',
  2282. ylabel => 'ms',
  2283. multiple => {
  2284. max_plots => 3,
  2285. max_per_plot => 10,
  2286. split_f => sub { max @{shift()} },
  2287. split_factor => 5,
  2288. legend_f => sub { 'SCHED &mdash; WAIT_MAX &mdash; ' . ceil(max @{shift()}) . 'MS'},
  2289. },
  2290. );
  2291. my @pids = uniq grep { defined and length } map { keys %{$_->{'/proc/pid/sched'}} } @$masterdb;
  2292. foreach my $pid (@pids) {
  2293. $plot->push(
  2294. [nonzero map {
  2295. if (exists $_->{'/proc/pid/sched'} && exists $_->{'/proc/pid/sched'}->{$pid}) {
  2296. my @entry = unpack('d*', $_->{'/proc/pid/sched'}->{$pid});
  2297. exists $entry[$SP::Endurance::Parser::schedmap{'se.statistics.wait_max'}] ?
  2298. $entry[$SP::Endurance::Parser::schedmap{'se.statistics.wait_max'}] :
  2299. undef
  2300. } else { undef }
  2301. } @$masterdb],
  2302. title => pid_to_cmdline($masterdb, $pid),
  2303. );
  2304. }
  2305. done_plotting $plot;
  2306. }
  2307. BEGIN { register_generator \&generate_plot_sched_wait_max; }
  2308. 1;