Skip to content

Commit 3caa84f

Browse files
authored
Merge pull request #6409 from BOINC/dpa_submit_stats
web (job submission): add batch features
2 parents 7132113 + db37703 commit 3caa84f

File tree

2 files changed

+259
-1
lines changed

2 files changed

+259
-1
lines changed

html/user/submit.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,8 +614,18 @@ function handle_query_batch($user) {
614614
break;
615615
}
616616
echo "<p>";
617+
show_button("submit_stats.php?action=flops_graph&batch_id=$batch_id",
618+
"Job runtimes"
619+
);
617620
show_button("submit.php?action=batch_stats&batch_id=$batch_id",
618-
"Show memory/disk usage statistics"
621+
"Memory/disk usage"
622+
);
623+
echo "<p>";
624+
show_button("submit_stats.php?action=err_host&batch_id=$batch_id",
625+
"Errors by host"
626+
);
627+
show_button("submit_stats.php?action=err_code&batch_id=$batch_id",
628+
"Errors by exit code"
619629
);
620630

621631
echo "<h2>Jobs</h2>\n";

html/user/submit_stats.php

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
<?php
2+
// This file is part of BOINC.
3+
// https://boinc.berkeley.edu
4+
// Copyright (C) 2025 University of California
5+
//
6+
// BOINC is free software; you can redistribute it and/or modify it
7+
// under the terms of the GNU Lesser General Public License
8+
// as published by the Free Software Foundation,
9+
// either version 3 of the License, or (at your option) any later version.
10+
//
11+
// BOINC is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
// See the GNU Lesser General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU Lesser General Public License
17+
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
18+
19+
// show various stats of batches:
20+
// err_host
21+
// list of hosts with the most errors
22+
// err_code
23+
// list of exit codes with most errors
24+
// flops_graph
25+
// histogram of average CPU hours
26+
27+
require_once('../inc/util.inc');
28+
require_once('../inc/result.inc');
29+
30+
function err_host($batch) {
31+
$results = BoincResult::enum_fields(
32+
'hostid',
33+
sprintf('batch=%d and outcome=%d',
34+
$batch->id,
35+
RESULT_OUTCOME_CLIENT_ERROR
36+
)
37+
);
38+
$x = [];
39+
foreach ($results as $r) {
40+
$id = $r->hostid;
41+
if (array_key_exists($id, $x)) {
42+
$x[$id] += 1;
43+
} else {
44+
$x[$id] = 1;
45+
}
46+
}
47+
if (!$x) error_page('That batch had no error results');
48+
page_head('Errors by host');
49+
text_start();
50+
arsort($x);
51+
start_table();
52+
table_header('Host', '# errors');
53+
$n = 0;
54+
foreach ($x as $id=>$count) {
55+
$host = BoincHost::lookup_id($id);
56+
table_row(
57+
"<a href=show_host_detail.php?hostid=$id>$host->domain_name</a>",
58+
sprintf(
59+
'<a href=submit_stats.php?action=host_list&host_id=%d&batch_id=%d>%d</a>',
60+
$id, $batch->id, $count
61+
)
62+
);
63+
if (++$n == 20) break;
64+
}
65+
text_end();
66+
end_table();
67+
page_tail();
68+
}
69+
70+
function err_code($batch) {
71+
$results = BoincResult::enum_fields(
72+
'exit_status',
73+
sprintf('batch=%d and outcome=%d',
74+
$batch->id,
75+
RESULT_OUTCOME_CLIENT_ERROR
76+
)
77+
);
78+
$x = [];
79+
foreach ($results as $r) {
80+
$id = $r->exit_status;
81+
if (array_key_exists($id, $x)) {
82+
$x[$id] += 1;
83+
} else {
84+
$x[$id] = 1;
85+
}
86+
}
87+
if (!$x) error_page('That batch had no error results');
88+
page_head('Errors by exit code');
89+
text_start();
90+
arsort($x);
91+
start_table();
92+
table_header('Code', '# errors');
93+
$n = 0;
94+
foreach ($x as $id=>$count) {
95+
table_row(
96+
exit_status_string($id),
97+
sprintf(
98+
'<a href=submit_stats.php?action=code_list&code=%d&batch_id=%d>%d</a>',
99+
$id, $batch->id, $count
100+
)
101+
);
102+
if (++$n == 20) break;
103+
}
104+
text_end();
105+
end_table();
106+
page_tail();
107+
}
108+
109+
// list of error results from a host
110+
//
111+
function host_list($batch_id, $host_id) {
112+
page_head("Errors for batch $batch_id, host $host_id");
113+
$results = BoincResult::enum(
114+
sprintf('batch=%d and hostid=%d and outcome=%d',
115+
$batch_id, $host_id, RESULT_OUTCOME_CLIENT_ERROR
116+
)
117+
);
118+
119+
start_table();
120+
table_header('Job instance', 'Exit status');
121+
foreach ($results as $r) {
122+
table_row(
123+
"<a href=result.php?resultid=$r->id>$r->name</a>",
124+
exit_status_string($r->exit_status)
125+
);
126+
}
127+
end_table();
128+
page_tail();
129+
}
130+
131+
// list of error results with given code
132+
//
133+
function code_list($batch_id, $code) {
134+
page_head("Errors for batch $batch_id, exit code $code");
135+
$results = BoincResult::enum(
136+
sprintf('batch=%d and exit_status=%d and outcome=%d',
137+
$batch_id, $code, RESULT_OUTCOME_CLIENT_ERROR
138+
)
139+
);
140+
141+
start_table();
142+
table_header('Job instance');
143+
foreach ($results as $r) {
144+
table_row(
145+
"<a href=result.php?resultid=$r->id>$r->name</a>"
146+
);
147+
}
148+
end_table();
149+
page_tail();
150+
}
151+
152+
function graph($data, $xlabel, $ylabel) {
153+
echo "
154+
<script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>
155+
<script type=\"text/javascript\">
156+
google.charts.load('current', {'packages':['corechart']});
157+
google.charts.setOnLoadCallback(drawChart);
158+
159+
function drawChart() {
160+
var data = google.visualization.arrayToDataTable([
161+
";
162+
echo "['$xlabel','$ylabel'],\n";
163+
foreach ($data as [$x, $y]) {
164+
echo "[$x, $y],\n";
165+
}
166+
echo "
167+
]);
168+
169+
var options = {
170+
title: 'Job runtime (hours)',
171+
legend: 'none'
172+
};
173+
174+
var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));
175+
176+
chart.draw(data, options);
177+
}
178+
</script>
179+
";
180+
echo "
181+
<div id=\"curve_chart\" style=\"width: 900px; height: 500px\"></div>
182+
";
183+
}
184+
185+
function flops_graph($batch) {
186+
page_head("Batch $batch->id job runtimes");
187+
echo "
188+
<p>
189+
Runtimes of completed jobs, normalized to an average (4.3 GFLOPS) computer.
190+
<p>
191+
";
192+
$results = BoincResult::enum_fields(
193+
'flops_estimate*elapsed_time as flops',
194+
sprintf('batch=%d and outcome=%d',
195+
$batch->id, RESULT_OUTCOME_SUCCESS
196+
)
197+
);
198+
$x = [];
199+
$min = 1e99;
200+
$max = 0;
201+
foreach ($results as $r) {
202+
$f = $r->flops / (4.3e9 *3600);
203+
if ($f > $max) $max = $f;
204+
if ($f < $min) $min = $f;
205+
$x[] = $f;
206+
}
207+
$n = 800;
208+
$count = [];
209+
for ($i=0; $i<$n; $i++) {
210+
$count[$i] = 0;
211+
}
212+
$range = $max - $min;
213+
foreach ($x as $f) {
214+
$d = intval($n*($f-$min)/$range);
215+
if ($d >= $n) $d = $n-1;
216+
$count[$d] += 1;
217+
}
218+
$data = [];
219+
for ($i=0; $i<$n; $i++) {
220+
$data[] = [$min+($i*$range)/$n, $count[$i]];
221+
}
222+
graph($data, 'hours', 'count');
223+
page_tail();
224+
}
225+
226+
$batch = BoincBatch::lookup_id(get_int('batch_id'));
227+
if (!$batch) error_page('no batch');
228+
switch(get_str('action')) {
229+
case 'err_host':
230+
err_host($batch);
231+
break;
232+
case 'err_code':
233+
err_code($batch);
234+
break;
235+
case 'host_list':
236+
host_list($batch->id, get_int('host_id'));
237+
break;
238+
case 'code_list':
239+
code_list($batch->id, get_int('code'));
240+
break;
241+
case 'flops_graph':
242+
flops_graph($batch);
243+
break;
244+
default:
245+
error_page('bad action');
246+
}
247+
248+
?>

0 commit comments

Comments
 (0)