refactor admin_acpuinfo

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann
2022-03-26 16:47:46 +01:00
parent 1d455ec4fb
commit 0b7443e875
3 changed files with 278 additions and 354 deletions

View File

@@ -1,5 +1,7 @@
<?php <?php
use Froxlor\UI\Panel\UI;
/* /*
* +----------------------------------------------------------------------+ * +----------------------------------------------------------------------+
* | APC | * | APC |
@@ -51,26 +53,6 @@ if ($page == 'showinfo') {
$time = time(); $time = time();
$log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "viewed admin_apcuinfo"); $log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "viewed admin_apcuinfo");
$passtime = $time - $cache['start_time'] > 0 ? $time - $cache['start_time'] : 1; // zero division
$mem_size = $mem['num_seg'] * $mem['seg_size'];
$mem_avail = $mem['avail_mem'];
$mem_used = $mem_size - $mem_avail;
$seg_size = bsize($mem['seg_size']);
$sharedmem = sprintf($lng['apcuinfo']['sharedmemval'], $mem['num_seg'], $seg_size, $cache['memory_type']);
$req_rate_user = sprintf("%.2f", $cache['num_hits'] ? (($cache['num_hits'] + $cache['num_misses']) / $passtime) : 0);
$hit_rate_user = sprintf("%.2f", $cache['num_hits'] ? (($cache['num_hits']) / $passtime) : 0);
$miss_rate_user = sprintf("%.2f", $cache['num_misses'] ? (($cache['num_misses']) / $passtime) : 0);
$insert_rate_user = sprintf("%.2f", $cache['num_inserts'] ? (($cache['num_inserts']) / $passtime) : 0);
$apcversion = phpversion('apcu');
$phpversion = phpversion();
$number_vars = $cache['num_entries'];
$starttime = date('Y-m-d H:i:s', $cache['start_time']);
$uptime_duration = duration($cache['start_time']);
$size_vars = bsize($cache['mem_size']);
$num_hits_and_misses = $cache['num_hits'] + $cache['num_misses'];
$num_hits_and_misses = 0 >= $num_hits_and_misses ? 1 : $num_hits_and_misses;
// check for possible empty values that are used in the templates // check for possible empty values that are used in the templates
if (!isset($cache['file_upload_progress'])) { if (!isset($cache['file_upload_progress'])) {
$cache['file_upload_progress'] = $lng['logger']['unknown']; $cache['file_upload_progress'] = $lng['logger']['unknown'];
@@ -80,18 +62,58 @@ if ($page == 'showinfo') {
$cache['num_expunges'] = $lng['logger']['unknown']; $cache['num_expunges'] = $lng['logger']['unknown'];
} }
$runtimelines = ''; $overview = [
'mem_size' => $mem['num_seg'] * $mem['seg_size'],
'mem_avail' => $mem['avail_mem'],
'mem_used' => ($mem['num_seg'] * $mem['seg_size']) - $mem['avail_mem'],
'seg_size' => bsize($mem['seg_size']),
'num_hits' => $cache['num_hits'],
'num_misses' => $cache['num_misses'],
'num_inserts' => $cache['num_inserts'],
'req_rate_user' => sprintf("%.2f", $cache['num_hits'] ? (($cache['num_hits'] + $cache['num_misses']) / ($time - $cache['start_time'])) : 0),
'hit_rate_user' => sprintf("%.2f", $cache['num_hits'] ? (($cache['num_hits']) / ($time - $cache['start_time'])) : 0),
'miss_rate_user' => sprintf("%.2f", $cache['num_misses'] ? (($cache['num_misses']) / ($time - $cache['start_time'])) : 0),
'insert_rate_user' => sprintf("%.2f", $cache['num_inserts'] ? (($cache['num_inserts']) / ($time - $cache['start_time'])) : 0),
'apcversion' => phpversion('apcu'),
'phpversion' => phpversion(),
'number_vars' => $cache['num_entries'],
'size_vars' => bsize($cache['mem_size']),
'num_hits_and_misses' => 0 >= ($cache['num_hits'] + $cache['num_misses']) ? 1 : ($cache['num_hits'] + $cache['num_misses']),
'file_upload_progress' => $cache['file_upload_progress'],
'num_expunges' => $cache['num_expunges'],
'host' => (function_exists('gethostname')
? gethostname()
: (php_uname('n')
?: (empty($_SERVER['SERVER_NAME'])
? $_SERVER['HOST_NAME']
: $_SERVER['SERVER_NAME']
)
)
),
'server' => $_SERVER['SERVER_SOFTWARE'] ?: '',
'start_time' => $cache['start_time'],
'uptime' => duration($cache['start_time'])
];
$overview['mem_used_percentage'] = number_format(($overview['mem_used'] / $overview['mem_avail']) * 100, 1);
$overview['num_hits_percentage'] = number_format(($overview['num_hits'] / $overview['num_hits_and_misses']) * 100, 1);
$overview['num_misses_percentage'] = number_format(($overview['num_misses'] / $overview['num_hits_and_misses']) * 100, 1);
$overview['readable'] = [
'mem_size' => bsize($overview['mem_size']),
'mem_avail' => bsize($overview['mem_avail']),
'mem_used' => bsize($overview['mem_used']),
'num_hits' => number_format($overview['num_hits']),
'num_misses' => number_format($overview['num_misses']),
'number_vars' => number_format($overview['number_vars']),
];
$overview['runtimelines'] = [];
foreach (ini_get_all('apcu') as $name => $v) { foreach (ini_get_all('apcu') as $name => $v) {
$value = $v['local_value']; $value = $v['local_value'];
eval("\$runtimelines.=\"" . \Froxlor\UI\Template::getTemplate("settings/apcuinfo/runtime_line") . "\";"); $overview['runtimelines'][$name] = $value;
} }
$freemem = bsize($mem_avail) . sprintf(" (%.1f%%)", $mem_avail * 100 / $mem_size); // Fragementation: (freeseg - 1) / total_seg
$usedmem = bsize($mem_used) . sprintf(" (%.1f%%)", $mem_used * 100 / $mem_size);
$hits = $cache['num_hits'] . @sprintf(" (%.1f%%)", $cache['num_hits'] * 100 / $num_hits_and_misses);
$misses = $cache['num_misses'] . @sprintf(" (%.1f%%)", $cache['num_misses'] * 100 / $num_hits_and_misses);
// Fragmentation: (freeseg - 1) / total_seg
$nseg = $freeseg = $fragsize = $freetotal = 0; $nseg = $freeseg = $fragsize = $freetotal = 0;
for ($i = 0; $i < $mem['num_seg']; $i++) { for ($i = 0; $i < $mem['num_seg']; $i++) {
$ptr = 0; $ptr = 0;
@@ -101,224 +123,46 @@ if ($page == 'showinfo') {
} }
$ptr = $block['offset'] + $block['size']; $ptr = $block['offset'] + $block['size'];
/* Only consider blocks <5M for the fragmentation % */ /* Only consider blocks <5M for the fragmentation % */
if ($block['size'] < (5 * 1024 * 1024)) if ($block['size'] < (5 * 1024 * 1024)) $fragsize += $block['size'];
$fragsize += $block['size'];
$freetotal += $block['size']; $freetotal += $block['size'];
} }
$freeseg += count($mem['block_lists'][$i]); $freeseg += count($mem['block_lists'][$i]);
} }
$overview['fragmentation'] = [];
if ($freeseg > 1) { if ($freeseg > 1) {
$frag = sprintf("%.2f%% (%s out of %s in %d fragments)", ($fragsize / $freetotal) * 100, bsize($fragsize), bsize($freetotal), $freeseg); $overview['fragmentation']['used_percentage'] = number_format(($fragsize / $freetotal) * 100, 1);
$overview['fragmentation']['used_bytes'] = $fragsize;
$overview['fragmentation']['total_bytes'] = $freetotal;
$overview['fragmentation']['num_frags'] = $freeseg;
$overview['fragmentation']['readable'] = [
'used_bytes' => bsize($fragsize),
'total_bytes' => bsize($freetotal),
'num_frags' => number_format($freeseg)
];
} else { } else {
$frag = "0%"; $overview['fragmentation'] = 0;
} }
foreach (ini_get_all('apcu') as $name => $v) { UI::view('settings/apcuinfo.html.twig', [
$value = $v['local_value']; 'apcuinfo' => $overview
]);
} }
$img_src1 = '';
$img_src2 = '';
$img_src3 = '';
if (graphics_avail()) {
$img_src = $linker->getLink(array(
'section' => 'apcuinfo',
'page' => 'img1',
'action' => mt_rand(0, 1000000)
));
eval("\$img_src1=\"" . \Froxlor\UI\Template::getTemplate("settings/apcuinfo/img_line") . "\";");
$img_src = $linker->getLink(array(
'section' => 'apcuinfo',
'page' => 'img2',
'action' => mt_rand(0, 1000000)
));
eval("\$img_src2=\"" . \Froxlor\UI\Template::getTemplate("settings/apcuinfo/img_line") . "\";");
$img_src = $linker->getLink(array(
'section' => 'apcuinfo',
'page' => 'img3',
'action' => mt_rand(0, 1000000)
));
eval("\$img_src3=\"" . \Froxlor\UI\Template::getTemplate("settings/apcuinfo/img_line") . "\";");
}
eval("echo \"" . \Froxlor\UI\Template::getTemplate("settings/apcuinfo/showinfo") . "\";");
} elseif ($page == 'img1') {
$mem = apcu_sma_info();
$size = 460;
$image = imagecreate($size + 5, $size + 5);
$col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
$col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
$col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
$col_black = imagecolorallocate($image, 0, 0, 0);
imagecolortransparent($image, $col_white);
$s = $mem['num_seg'] * $mem['seg_size'];
$a = $mem['avail_mem'];
$x = $y = $size / 2;
$fuzz = 0.000001;
// This block of code creates the pie chart. It is a lot more complex than you
// would expect because we try to visualize any memory fragmentation as well.
$angle_from = 0;
$string_placement = array();
for ($i = 0; $i < $mem['num_seg']; $i ++) {
$ptr = 0;
$free = $mem['block_lists'][$i];
uasort($free, 'block_sort');
foreach ($free as $block) {
if ($block['offset'] != $ptr) { // Used block
$angle_to = $angle_from + ($block['offset'] - $ptr) / $s;
if (($angle_to + $fuzz) > 1)
$angle_to = 1;
if (($angle_to * 360) - ($angle_from * 360) >= 1) {
fill_arc($image, $x, $y, $size, $angle_from * 360, $angle_to * 360, $col_black, $col_red);
if (($angle_to - $angle_from) > 0.05) {
array_push($string_placement, array(
$angle_from,
$angle_to
));
}
}
$angle_from = $angle_to;
}
$angle_to = $angle_from + ($block['size']) / $s;
if (($angle_to + $fuzz) > 1)
$angle_to = 1;
if (($angle_to * 360) - ($angle_from * 360) >= 1) {
fill_arc($image, $x, $y, $size, $angle_from * 360, $angle_to * 360, $col_black, $col_green);
if (($angle_to - $angle_from) > 0.05) {
array_push($string_placement, array(
$angle_from,
$angle_to
));
}
}
$angle_from = $angle_to;
$ptr = $block['offset'] + $block['size'];
}
if ($ptr < $mem['seg_size']) { // memory at the end
$angle_to = $angle_from + ($mem['seg_size'] - $ptr) / $s;
if (($angle_to + $fuzz) > 1)
$angle_to = 1;
fill_arc($image, $x, $y, $size, $angle_from * 360, $angle_to * 360, $col_black, $col_red);
if (($angle_to - $angle_from) > 0.05) {
array_push($string_placement, array(
$angle_from,
$angle_to
));
}
}
}
foreach ($string_placement as $angle) {
text_arc($image, $x, $y, $size, $angle[0] * 360, $angle[1] * 360, $col_black, bsize($s * ($angle[1] - $angle[0])));
}
header("Content-type: image/png");
imagepng($image);
exit();
} elseif ($page == 'img2') {
$cache = apcu_cache_info();
$size = $horizontal_bar_size;
$image = imagecreate($size + 5, 140);
$col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
$col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
$col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
$col_black = imagecolorallocate($image, 0, 0, 0);
imagecolortransparent($image, $col_white);
$s = $cache['num_hits'] + $cache['num_misses'];
$a = $cache['num_hits'];
fill_box($image, 1, 10, $s ? ($a * ($size - 21) / $s) : $size, 50, $col_black, $col_green /* , sprintf("%.1f%%", $s ? $cache['num_hits'] * 100 / $s : 0) */
);
fill_box($image, 1, 80, $s ? max(4, ($s - $a) * ($size - 21) / $s) : $size, 50, $col_black, $col_red /* , sprintf("%.1f%%", $s ? $cache['num_misses'] * 100 / $s : 0) */
);
header("Content-type: image/png");
imagepng($image);
exit();
} elseif ($page == 'img3') {
$mem = apcu_sma_info();
$size = $horizontal_bar_size;
$image = imagecreate($size, 70);
$col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
$col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
$col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
$col_black = imagecolorallocate($image, 0, 0, 0);
imagecolortransparent($image, $col_white);
$s = $mem['num_seg'] * $mem['seg_size'];
$a = $mem['avail_mem'];
$x = 10;
$y = 0;
// This block of code creates the bar chart. It is a lot more complex than you
// would expect because we try to visualize any memory fragmentation as well.
for ($i = 0; $i < $mem['num_seg']; $i ++) {
$ptr = 0;
$free = $mem['block_lists'][$i];
uasort($free, 'block_sort');
foreach ($free as $block) {
if ($block['offset'] != $ptr) { // Used block
$h = ($size - 5) * ($block['offset'] - $ptr) / $s;
if ($h > 0) {
fill_box($image, $y, $x, $h, 50, $col_black, $col_red);
}
$y += $h;
}
$h = ($size - 5) * ($block['size']) / $s;
if ($h > 0) {
fill_box($image, $y, $x, $h, 50, $col_black, $col_green);
}
$y += $h;
$ptr = $block['offset'] + $block['size'];
}
if ($ptr < $mem['seg_size']) { // memory at the end
$h = ($size - 5) * ($mem['seg_size'] - $ptr) / $s;
if ($h > 0) {
fill_box($image, $y, $x, $h, 50, $col_black, $col_red, bsize($mem['seg_size'] - $ptr), $j ++);
}
}
}
header("Content-type: image/png");
imagepng($image);
exit();
}
function graphics_avail()
{
return extension_loaded('gd');
}
// pretty printer for byte values // pretty printer for byte values
// function bsize($size)
function bsize($s)
{ {
foreach (array( $i = 0;
'', $val = ['b', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
'K', while (($size / 1024) > 1) {
'M', $size /= 1024;
'G' ++$i;
) as $i => $k) {
if ($s < 1024)
break;
$s /= 1024;
} }
return sprintf("%5.1f %sBytes", $s, $k); return sprintf(
'%.2f%s%s',
$size,
'',
$val[$i]
);
} }
function duration($ts) function duration($ts)
@@ -331,114 +175,15 @@ function duration($ts)
$hours = (int)(($rem) / 3600) - $days * 24 - $weeks * 7 * 24; $hours = (int)(($rem) / 3600) - $days * 24 - $weeks * 7 * 24;
$mins = (int)(($rem) / 60) - $hours * 60 - $days * 24 * 60 - $weeks * 7 * 24 * 60; $mins = (int)(($rem) / 60) - $hours * 60 - $days * 24 * 60 - $weeks * 7 * 24 * 60;
$str = ''; $str = '';
if ($years == 1) if ($years == 1) $str .= "$years year, ";
$str .= "$years year, "; if ($years > 1) $str .= "$years years, ";
if ($years > 1) if ($weeks == 1) $str .= "$weeks week, ";
$str .= "$years years, "; if ($weeks > 1) $str .= "$weeks weeks, ";
if ($weeks == 1) if ($days == 1) $str .= "$days day,";
$str .= "$weeks week, "; if ($days > 1) $str .= "$days days,";
if ($weeks > 1) if ($hours == 1) $str .= " $hours hour and";
$str .= "$weeks weeks, "; if ($hours > 1) $str .= " $hours hours and";
if ($days == 1) if ($mins == 1) $str .= " 1 minute";
$str .= "$days day,"; else $str .= " $mins minutes";
if ($days > 1)
$str .= "$days days,";
if ($hours == 1)
$str .= " $hours hour and";
if ($hours > 1)
$str .= " $hours hours and";
if ($mins == 1)
$str .= " 1 minute";
else
$str .= " $mins minutes";
return $str; return $str;
} }
function block_sort($array1, $array2)
{
if ($array1['offset'] > $array2['offset']) {
return 1;
} else {
return - 1;
}
}
function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1, $color2, $text = '', $placeindex = 0)
{
$r = $diameter / 2;
$w = deg2rad((360 + $start + ($end - $start) / 2) % 360);
if (function_exists("imagefilledarc")) {
// exists only if GD 2.0.1 is available
imagefilledarc($im, $centerX + 1, $centerY + 1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE);
imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE);
imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL | IMG_ARC_EDGED);
} else {
imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2);
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start + 1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end - 1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
imagefill($im, $centerX + $r * cos($w) / 2, $centerY + $r * sin($w) / 2, $color2);
}
if ($text) {
if ($placeindex > 0) {
imageline($im, $centerX + $r * cos($w) / 2, $centerY + $r * sin($w) / 2, $diameter, $placeindex * 12, $color1);
imagestring($im, 4, $diameter, $placeindex * 12, $text, $color1);
} else {
imagestring($im, 4, $centerX + $r * cos($w) / 2, $centerY + $r * sin($w) / 2, $text, $color1);
}
}
}
function text_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1, $text, $placeindex = 0)
{
$r = $diameter / 2;
$w = deg2rad((360 + $start + ($end - $start) / 2) % 360);
if ($placeindex > 0) {
imageline($im, $centerX + $r * cos($w) / 2, $centerY + $r * sin($w) / 2, $diameter, $placeindex * 12, $color1);
imagestring($im, 4, $diameter, $placeindex * 12, $text, $color1);
} else {
imagestring($im, 4, $centerX + $r * cos($w) / 2, $centerY + $r * sin($w) / 2, $text, $color1);
}
}
function fill_box($im, $x, $y, $w, $h, $color1, $color2, $text = '', $placeindex = '')
{
global $col_black;
$x1 = $x + $w - 1;
$y1 = $y + $h - 1;
imagerectangle($im, $x, $y1, $x1 + 1, $y + 1, $col_black);
if ($y1 > $y)
imagefilledrectangle($im, $x, $y, $x1, $y1, $color2);
else
imagefilledrectangle($im, $x, $y1, $x1, $y, $color2);
imagerectangle($im, $x, $y1, $x1, $y, $color1);
if ($text) {
if ($placeindex > 0) {
if ($placeindex < 16) {
$px = 5;
$py = $placeindex * 12 + 6;
imagefilledrectangle($im, $px + 90, $py + 3, $px + 90 - 4, $py - 3, $color2);
imageline($im, $x, $y + $h / 2, $px + 90, $py, $color2);
imagestring($im, 2, $px, $py - 6, $text, $color1);
} else {
if ($placeindex < 31) {
$px = $x + 40 * 2;
$py = ($placeindex - 15) * 12 + 6;
} else {
$px = $x + 40 * 2 + 100 * intval(($placeindex - 15) / 15);
$py = ($placeindex % 15) * 12 + 6;
}
imagefilledrectangle($im, $px, $py + 3, $px - 4, $py - 3, $color2);
imageline($im, $x + $w, $y + $h / 2, $px, $py, $color2);
imagestring($im, 2, $px + 2, $py - 6, $text, $color1);
}
} else {
imagestring($im, 4, $x + 5, $y1 - 16, $text, $color1);
}
}
}

View File

@@ -1782,12 +1782,15 @@ $lng['apcuinfo']['missrate'] = 'Miss Rate';
$lng['apcuinfo']['insrate'] = 'Insert Rate'; $lng['apcuinfo']['insrate'] = 'Insert Rate';
$lng['apcuinfo']['cachefull'] = 'Cache full count'; $lng['apcuinfo']['cachefull'] = 'Cache full count';
$lng['apcuinfo']['runtime'] = 'Runtime Settings'; $lng['apcuinfo']['runtime'] = 'Runtime Settings';
$lng['apcuinfo']['memnote'] = 'Memory Usage <font size=-2>(multiple slices indicate fragments)</font>'; $lng['apcuinfo']['memnote'] = 'Memory Usage';
$lng['apcuinfo']['total'] = 'Total';
$lng['apcuinfo']['free'] = 'Free'; $lng['apcuinfo']['free'] = 'Free';
$lng['apcuinfo']['used'] = 'Used'; $lng['apcuinfo']['used'] = 'Used';
$lng['apcuinfo']['hitmiss'] = 'Hits & Misses'; $lng['apcuinfo']['hitmiss'] = 'Hits & Misses';
$lng['apcuinfo']['detailmem'] = 'Detailed Memory Usage and Fragmentation'; $lng['apcuinfo']['detailmem'] = 'Detailed Memory Usage and Fragmentation';
$lng['apcuinfo']['fragment'] = 'Fragmentation'; $lng['apcuinfo']['fragment'] = 'Fragmentation';
$lng['apcuinfo']['nofragment'] = 'No fragmentation';
$lng['apcuinfo']['fragments'] = 'Fragments';
// Added for opcache info // Added for opcache info
$lng['admin']['opcacheinfo'] = 'OPcache Info'; $lng['admin']['opcacheinfo'] = 'OPcache Info';

View File

@@ -0,0 +1,176 @@
{% extends "Froxlor/userarea.html.twig" %}
{% block heading %}
<h5>
<i class="fa-solid fa-hard-drive me-1"></i>
{{ lng('admin.apcuinfo') }}
</h5>
{% endblock %}
{% block actions %}
<a class="btn btn-warning" href="{{ linker({'section':'apcuinfo','page':'showinfo','action':'delete'}) }}">
<i class="fa-solid fa-trash-can me-1"></i>
{{ lng('apcuinfo.clearcache') }}
</a>
{% endblock %}
{% block content %}
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 mb-4">
<div class="col">
<div class="card h-100 mb-3">
<div class="card-body">
<h5 class="card-title">{{ lng('apcuinfo.memnote') }}</h5>
<div class="progress position-relative">
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.mem_used_percentage }}%" aria-valuenow="{{ apcuinfo.mem_used }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.mem_avail }}"></div>
<small class="justify-content-center d-flex position-absolute w-100">{{ apcuinfo.mem_used_percentage }}%</small>
</div>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.total') }}
<span class="badge bg-secondary">{{ apcuinfo.readable.mem_size }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.used') }}
<span class="badge bg-secondary">{{ apcuinfo.readable.mem_used }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.free') }}
<span class="badge bg-secondary">{{ apcuinfo.readable.mem_avail }}</span>
</li>
</ul>
</div>
</div>
<div class="col">
<div class="card h-100 mb-3">
<div class="card-body">
<h5 class="card-title">{{ lng('apcuinfo.hitmiss') }}</h5>
<div class="progress position-relative">
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.num_hits_percentage }}%" aria-valuenow="{{ apcuinfo.num_hits }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.num_hits_and_misses }}"></div>
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ 100 - apcuinfo.num_misses_percentage }}%" aria-valuenow="{{ apcuinfo.num_misses }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.num_hits_and_misses }}"></div>
<small class="justify-content-center d-flex position-absolute w-100">{{ apcuinfo.num_hits_percentage }}%</small>
</div>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.hit') }}
<span class="badge bg-secondary">{{ apcuinfo.readable.num_hits }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.miss') }}
<span class="badge bg-secondary">{{ apcuinfo.readable.num_misses }}</span>
</li>
</ul>
</div>
</div>
<div class="col">
<div class="card h-100 mb-3">
<div class="card-body">
<h5 class="card-title">{{ lng('apcuinfo.cachetitle') }}</h5>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.cvar') }}
<span class="badge bg-secondary">{{ apcuinfo.readable.number_vars }}
({{ apcuinfo.size_vars }})</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.reqrate') }}
<span class="badge bg-secondary">{{ apcuinfo.req_rate_user }}
{{ lng('apcuinfo.creqsec') }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.hitrate') }}
<span class="badge bg-secondary">{{ apcuinfo.hit_rate_user }}
{{ lng('apcuinfo.creqsec') }}</span>
</li>
</ul>
</div>
</div>
<div class="col">
<div class="card h-100 mb-3">
<div class="card-body">
<h5 class="card-title">{{ lng('apcuinfo.detailmem') }}</h5>
{% if apcuinfo.fragmentation is not iterable %}
{{ lng('apcuinfo.nofragment') }}
{% endif %}
</div>
{% if apcuinfo.fragmentation is iterable %}
<div class="progress position-relative">
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.fragmentation.used_percentage }}%" aria-valuenow="{{ apcuinfo.fragmentation.used_bytes }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.fragmentation.total_bytes }}"></div>
<small class="justify-content-center d-flex position-absolute w-100">{{ apcuinfo.fragmentation.used_percentage }}%</small>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.total') }}
<span class="badge bg-secondary">{{ apcuinfo.fragmentation.readable.total_bytes }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.used') }}
<span class="badge bg-secondary">{{ apcuinfo.fragmentation.readable.used_bytes }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ lng('apcuinfo.fragments') }}
<span class="badge bg-secondary">{{ apcuinfo.fragmentation.readable.num_frags }}</span>
</li>
</ul>
{% endif %}
</div>
</div>
</div>
<div class="row row-cols-1 row-cols-md-2">
<div class="col">
<div class="card table-responsive mb-3">
<table class="table table-borderless table-striped align-middle mb-0 px-3">
<tbody>
<tr>
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.generaltitle') }}</th>
</tr>
<tr>
<th class="fw-bold" scope="row">{{ lng('apcuinfo.version') }}</th>
<td class="text-end">{{ apcuinfo.apcversion }}</td>
</tr>
<tr>
<th class="fw-bold" scope="row">{{ lng('apcuinfo.phpversion') }}</th>
<td class="text-end">{{ apcuinfo.phpversion }}</td>
</tr>
<tr>
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
<td class="text-end">{{ apcuinfo.host }}</td>
</tr>
<tr>
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
<td class="text-end">{{ apcuinfo.server }}</td>
</tr>
<tr>
<th class="fw-bold" scope="row">{{ lng('apcuinfo.start') }}</th>
<td class="text-end">{{ apcuinfo.start_time|date('d.m.Y H:i:s') }}</td>
</tr>
<tr>
<th class="fw-bold" scope="row">{{ lng('apcuinfo.uptime') }}</th>
<td class="text-end">{{ apcuinfo.uptime }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col">
<div class="card table-responsive">
<table class="table table-borderless table-striped align-middle mb-0 px-3">
<tbody>
<tr>
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.runtime') }}</th>
</tr>
{% for k,v in apcuinfo.runtimelines %}
<tr>
<th class="fw-bold" scope="row">{{ k|raw }}</th>
<td class="text-end">{{ v|raw }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}