PageRenderTime 43ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full file

  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 F…

Large files files are truncated, but you can click here to view the full file