Derek Beckett | e9b63ce | 2020-09-15 13:09:04 -0700 | [diff] [blame^] | 1 | # Lint as: python2, python3 |
Vivia Nikolaidou | 26bc65a | 2012-07-17 15:32:13 +0100 | [diff] [blame] | 2 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 6 | """Library to run fio scripts. |
| 7 | |
| 8 | fio_runner launch fio and collect results. |
| 9 | The output dictionary can be add to autotest keyval: |
| 10 | results = {} |
| 11 | results.update(fio_util.fio_runner(job_file, env_vars)) |
| 12 | self.write_perf_keyval(results) |
| 13 | |
| 14 | Decoding class can be invoked independently. |
| 15 | |
| 16 | """ |
| 17 | |
Derek Beckett | e9b63ce | 2020-09-15 13:09:04 -0700 | [diff] [blame^] | 18 | from __future__ import absolute_import |
| 19 | from __future__ import division |
| 20 | from __future__ import print_function |
| 21 | |
Allen Li | 2c32d6b | 2017-02-03 15:28:10 -0800 | [diff] [blame] | 22 | import json |
| 23 | import logging |
| 24 | import re |
| 25 | |
Derek Beckett | e9b63ce | 2020-09-15 13:09:04 -0700 | [diff] [blame^] | 26 | import six |
| 27 | from six.moves import range |
| 28 | |
Allen Li | 2c32d6b | 2017-02-03 15:28:10 -0800 | [diff] [blame] | 29 | import common |
| 30 | from autotest_lib.client.bin import utils |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 31 | |
Derek Beckett | e9b63ce | 2020-09-15 13:09:04 -0700 | [diff] [blame^] | 32 | |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 33 | class fio_graph_generator(): |
| 34 | """ |
| 35 | Generate graph from fio log that created when specified these options. |
| 36 | - write_bw_log |
| 37 | - write_iops_log |
| 38 | - write_lat_log |
| 39 | |
| 40 | The following limitations apply |
| 41 | - Log file name must be in format jobname_testpass |
| 42 | - Graph is generate using Google graph api -> Internet require to view. |
| 43 | """ |
| 44 | |
| 45 | html_head = """ |
| 46 | <html> |
| 47 | <head> |
| 48 | <script type="text/javascript" src="https://www.google.com/jsapi"></script> |
| 49 | <script type="text/javascript"> |
| 50 | google.load("visualization", "1", {packages:["corechart"]}); |
| 51 | google.setOnLoadCallback(drawChart); |
| 52 | function drawChart() { |
| 53 | """ |
| 54 | |
| 55 | html_tail = """ |
| 56 | var chart_div = document.getElementById('chart_div'); |
| 57 | var chart = new google.visualization.ScatterChart(chart_div); |
| 58 | chart.draw(data, options); |
| 59 | } |
| 60 | </script> |
| 61 | </head> |
| 62 | <body> |
| 63 | <div id="chart_div" style="width: 100%; height: 100%;"></div> |
| 64 | </body> |
| 65 | </html> |
| 66 | """ |
| 67 | |
| 68 | h_title = { True: 'Percentile', False: 'Time (s)' } |
| 69 | v_title = { 'bw' : 'Bandwidth (KB/s)', |
| 70 | 'iops': 'IOPs', |
| 71 | 'lat' : 'Total latency (us)', |
| 72 | 'clat': 'Completion latency (us)', |
| 73 | 'slat': 'Submission latency (us)' } |
| 74 | graph_title = { 'bw' : 'bandwidth', |
| 75 | 'iops': 'IOPs', |
| 76 | 'lat' : 'total latency', |
| 77 | 'clat': 'completion latency', |
| 78 | 'slat': 'submission latency' } |
| 79 | |
| 80 | test_name = '' |
| 81 | test_type = '' |
| 82 | pass_list = '' |
| 83 | |
| 84 | @classmethod |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 85 | def _parse_log_file(cls, file_name, pass_index, pass_count, percentile): |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 86 | """ |
| 87 | Generate row for google.visualization.DataTable from one log file. |
| 88 | Log file is the one that generated using write_{bw,lat,iops}_log |
| 89 | option in the FIO job file. |
| 90 | |
| 91 | The fio log file format is timestamp, value, direction, blocksize |
| 92 | The output format for each row is { c: list of { v: value} } |
| 93 | |
| 94 | @param file_name: log file name to read data from |
| 95 | @param pass_index: index of current run pass |
| 96 | @param pass_count: number of all test run passes |
| 97 | @param percentile: flag to use percentile as key instead of timestamp |
| 98 | |
| 99 | @return: list of data rows in google.visualization.DataTable format |
| 100 | """ |
| 101 | # Read data from log |
| 102 | with open(file_name, 'r') as f: |
| 103 | data = [] |
| 104 | |
| 105 | for line in f.readlines(): |
| 106 | if not line: |
| 107 | break |
| 108 | t, v, _, _ = [int(x) for x in line.split(', ')] |
| 109 | data.append([t / 1000.0, v]) |
| 110 | |
| 111 | # Sort & calculate percentile |
| 112 | if percentile: |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 113 | data.sort(key=lambda x: x[1]) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 114 | l = len(data) |
| 115 | for i in range(l): |
| 116 | data[i][0] = 100 * (i + 0.5) / l |
| 117 | |
| 118 | # Generate the data row |
| 119 | all_row = [] |
| 120 | row = [None] * (pass_count + 1) |
| 121 | for d in data: |
| 122 | row[0] = {'v' : '%.3f' % d[0]} |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 123 | row[pass_index + 1] = {'v': d[1]} |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 124 | all_row.append({'c': row[:]}) |
| 125 | |
| 126 | return all_row |
| 127 | |
| 128 | @classmethod |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 129 | def _gen_data_col(cls, pass_list, percentile): |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 130 | """ |
| 131 | Generate col for google.visualization.DataTable |
| 132 | |
| 133 | The output format is list of dict of label and type. In this case, |
| 134 | type is always number. |
| 135 | |
| 136 | @param pass_list: list of test run passes |
| 137 | @param percentile: flag to use percentile as key instead of timestamp |
| 138 | |
| 139 | @return: list of column in google.visualization.DataTable format |
| 140 | """ |
| 141 | if percentile: |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 142 | col_name_list = ['percentile'] + [p[0] for p in pass_list] |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 143 | else: |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 144 | col_name_list = ['time'] + [p[0] for p in pass_list] |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 145 | |
| 146 | return [{'label': name, 'type': 'number'} for name in col_name_list] |
| 147 | |
| 148 | @classmethod |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 149 | def _gen_data_row(cls, test_type, pass_list, percentile): |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 150 | """ |
| 151 | Generate row for google.visualization.DataTable by generate all log |
| 152 | file name and call _parse_log_file for each file |
| 153 | |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 154 | @param test_type: type of value collected for current test. i.e. IOPs |
| 155 | @param pass_list: list of run passes for current test |
| 156 | @param percentile: flag to use percentile as key instead of timestamp |
| 157 | |
| 158 | @return: list of data rows in google.visualization.DataTable format |
| 159 | """ |
| 160 | all_row = [] |
| 161 | pass_count = len(pass_list) |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 162 | for pass_index, log_file_name in enumerate([p[1] for p in pass_list]): |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 163 | all_row.extend(cls._parse_log_file(log_file_name, pass_index, |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 164 | pass_count, percentile)) |
| 165 | return all_row |
| 166 | |
| 167 | @classmethod |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 168 | def _write_data(cls, f, test_type, pass_list, percentile): |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 169 | """ |
| 170 | Write google.visualization.DataTable object to output file. |
| 171 | https://developers.google.com/chart/interactive/docs/reference |
| 172 | |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 173 | @param f: html file to update |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 174 | @param test_type: type of value collected for current test. i.e. IOPs |
| 175 | @param pass_list: list of run passes for current test |
| 176 | @param percentile: flag to use percentile as key instead of timestamp |
| 177 | """ |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 178 | col = cls._gen_data_col(pass_list, percentile) |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 179 | row = cls._gen_data_row(test_type, pass_list, percentile) |
| 180 | data_dict = {'cols' : col, 'rows' : row} |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 181 | |
| 182 | f.write('var data = new google.visualization.DataTable(') |
| 183 | json.dump(data_dict, f) |
| 184 | f.write(');\n') |
| 185 | |
| 186 | @classmethod |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 187 | def _write_option(cls, f, test_name, test_type, percentile): |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 188 | """ |
| 189 | Write option to render scatter graph to output file. |
| 190 | https://google-developers.appspot.com/chart/interactive/docs/gallery/scatterchart |
| 191 | |
| 192 | @param test_name: name of current workload. i.e. randwrite |
| 193 | @param test_type: type of value collected for current test. i.e. IOPs |
| 194 | @param percentile: flag to use percentile as key instead of timestamp |
| 195 | """ |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 196 | option = {'pointSize': 1} |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 197 | if percentile: |
| 198 | option['title'] = ('Percentile graph of %s for %s workload' % |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 199 | (cls.graph_title[test_type], test_name)) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 200 | else: |
| 201 | option['title'] = ('Graph of %s for %s workload over time' % |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 202 | (cls.graph_title[test_type], test_name)) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 203 | |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 204 | option['hAxis'] = {'title': cls.h_title[percentile]} |
| 205 | option['vAxis'] = {'title': cls.v_title[test_type]} |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 206 | |
| 207 | f.write('var options = ') |
| 208 | json.dump(option, f) |
| 209 | f.write(';\n') |
| 210 | |
| 211 | @classmethod |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 212 | def _write_graph(cls, test_name, test_type, pass_list, percentile=False): |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 213 | """ |
| 214 | Generate graph for test name / test type |
| 215 | |
| 216 | @param test_name: name of current workload. i.e. randwrite |
| 217 | @param test_type: type of value collected for current test. i.e. IOPs |
| 218 | @param pass_list: list of run passes for current test |
| 219 | @param percentile: flag to use percentile as key instead of timestamp |
| 220 | """ |
| 221 | logging.info('fio_graph_generator._write_graph %s %s %s', |
| 222 | test_name, test_type, str(pass_list)) |
| 223 | |
| 224 | |
| 225 | if percentile: |
| 226 | out_file_name = '%s_%s_percentile.html' % (test_name, test_type) |
| 227 | else: |
| 228 | out_file_name = '%s_%s.html' % (test_name, test_type) |
| 229 | |
| 230 | with open(out_file_name, 'w') as f: |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 231 | f.write(cls.html_head) |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 232 | cls._write_data(f, test_type, pass_list, percentile) |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 233 | cls._write_option(f, test_name, test_type, percentile) |
| 234 | f.write(cls.html_tail) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 235 | |
| 236 | def __init__(self, test_name, test_type, pass_list): |
| 237 | """ |
| 238 | @param test_name: name of current workload. i.e. randwrite |
| 239 | @param test_type: type of value collected for current test. i.e. IOPs |
| 240 | @param pass_list: list of run passes for current test |
| 241 | """ |
| 242 | self.test_name = test_name |
| 243 | self.test_type = test_type |
| 244 | self.pass_list = pass_list |
| 245 | |
| 246 | def run(self): |
| 247 | """ |
| 248 | Run the graph generator. |
| 249 | """ |
| 250 | self._write_graph(self.test_name, self.test_type, self.pass_list, False) |
| 251 | self._write_graph(self.test_name, self.test_type, self.pass_list, True) |
| 252 | |
| 253 | |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 254 | def fio_parse_dict(d, prefix): |
| 255 | """ |
| 256 | Parse fio json dict |
| 257 | |
| 258 | Recursively flaten json dict to generate autotest perf dict |
| 259 | |
| 260 | @param d: input dict |
| 261 | @param prefix: name prefix of the key |
| 262 | """ |
| 263 | |
| 264 | # No need to parse something that didn't run such as read stat in write job. |
| 265 | if 'io_bytes' in d and d['io_bytes'] == 0: |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 266 | return {} |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 267 | |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 268 | results = {} |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 269 | for k, v in d.items(): |
| 270 | |
| 271 | # remove >, >=, <, <= |
| 272 | for c in '>=<': |
| 273 | k = k.replace(c, '') |
| 274 | |
| 275 | key = prefix + '_' + k |
| 276 | |
| 277 | if type(v) is dict: |
| 278 | results.update(fio_parse_dict(v, key)) |
| 279 | else: |
| 280 | results[key] = v |
| 281 | return results |
| 282 | |
| 283 | |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 284 | def fio_parser(lines, prefix=None): |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 285 | """ |
| 286 | Parse the json fio output |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 287 | |
| 288 | This collects all metrics given by fio and labels them according to unit |
| 289 | of measurement and test case name. |
| 290 | |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 291 | @param lines: text output of json fio output. |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 292 | @param prefix: prefix for result keys. |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 293 | """ |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 294 | results = {} |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 295 | fio_dict = json.loads(lines) |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 296 | |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 297 | if prefix: |
| 298 | prefix = prefix + '_' |
| 299 | else: |
| 300 | prefix = '' |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 301 | |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 302 | results[prefix + 'fio_version'] = fio_dict['fio version'] |
| 303 | |
| 304 | if 'disk_util' in fio_dict: |
| 305 | results.update(fio_parse_dict(fio_dict['disk_util'][0], |
| 306 | prefix + 'disk')) |
| 307 | |
| 308 | for job in fio_dict['jobs']: |
| 309 | job_prefix = '_' + prefix + job['jobname'] |
| 310 | job.pop('jobname') |
| 311 | |
| 312 | |
Derek Beckett | e9b63ce | 2020-09-15 13:09:04 -0700 | [diff] [blame^] | 313 | for k, v in six.iteritems(job): |
Gwendal Grignou | 9854631 | 2017-03-21 17:02:22 -0700 | [diff] [blame] | 314 | # Igonre "job options", its alphanumerc keys confuses tko. |
| 315 | # Besides, these keys are redundant. |
| 316 | if k == 'job options': |
| 317 | continue |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 318 | results.update(fio_parse_dict({k:v}, job_prefix)) |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 319 | |
| 320 | return results |
| 321 | |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 322 | def fio_generate_graph(): |
| 323 | """ |
| 324 | Scan for fio log file in output directory and send data to generate each |
| 325 | graph to fio_graph_generator class. |
| 326 | """ |
| 327 | log_types = ['bw', 'iops', 'lat', 'clat', 'slat'] |
| 328 | |
| 329 | # move fio log to result dir |
| 330 | for log_type in log_types: |
| 331 | logging.info('log_type %s', log_type) |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 332 | logs = utils.system_output('ls *_%s.*log' % log_type, ignore_status=True) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 333 | if not logs: |
| 334 | continue |
| 335 | |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 336 | pattern = r"""(?P<jobname>.*)_ # jobname |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 337 | ((?P<runpass>p\d+)_|) # pass |
| 338 | (?P<type>bw|iops|lat|clat|slat) # type |
| 339 | (.(?P<thread>\d+)|) # thread id for newer fio. |
| 340 | .log |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 341 | """ |
| 342 | matcher = re.compile(pattern, re.X) |
| 343 | |
| 344 | pass_list = [] |
| 345 | current_job = '' |
| 346 | |
| 347 | for log in logs.split(): |
| 348 | match = matcher.match(log) |
| 349 | if not match: |
| 350 | logging.warn('Unknown log file %s', log) |
| 351 | continue |
| 352 | |
| 353 | jobname = match.group('jobname') |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 354 | runpass = match.group('runpass') or '1' |
| 355 | if match.group('thread'): |
| 356 | runpass += '_' + match.group('thread') |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 357 | |
| 358 | # All files for particular job name are group together for create |
| 359 | # graph that can compare performance between result from each pass. |
| 360 | if jobname != current_job: |
| 361 | if pass_list: |
| 362 | fio_graph_generator(current_job, log_type, pass_list).run() |
| 363 | current_job = jobname |
| 364 | pass_list = [] |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 365 | pass_list.append((runpass, log)) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 366 | |
| 367 | if pass_list: |
| 368 | fio_graph_generator(current_job, log_type, pass_list).run() |
| 369 | |
| 370 | |
Gwendal Grignou | 08de238 | 2015-02-06 17:42:16 -0800 | [diff] [blame] | 371 | cmd = 'mv *_%s.*log results' % log_type |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 372 | utils.run(cmd, ignore_status=True) |
| 373 | utils.run('mv *.html results', ignore_status=True) |
| 374 | |
| 375 | |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 376 | def fio_runner(test, job, env_vars, |
| 377 | name_prefix=None, |
| 378 | graph_prefix=None): |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 379 | """ |
| 380 | Runs fio. |
| 381 | |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 382 | Build a result keyval and performence json. |
| 383 | The JSON would look like: |
| 384 | {"description": "<name_prefix>_<modle>_<size>G", |
| 385 | "graph": "<graph_prefix>_1m_write_wr_lat_99.00_percent_usec", |
| 386 | "higher_is_better": false, "units": "us", "value": "xxxx"} |
| 387 | {... |
| 388 | |
| 389 | |
Puthikorn Voravootivat | 425b1a7 | 2014-05-14 17:33:27 -0700 | [diff] [blame] | 390 | @param test: test to upload perf value |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 391 | @param job: fio config file to use |
| 392 | @param env_vars: environment variable fio will substituete in the fio |
| 393 | config file. |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 394 | @param name_prefix: prefix of the descriptions to use in chrome perfi |
| 395 | dashboard. |
| 396 | @param graph_prefix: prefix of the graph name in chrome perf dashboard |
| 397 | and result keyvals. |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 398 | @return fio results. |
| 399 | |
| 400 | """ |
| 401 | |
| 402 | # running fio with ionice -c 3 so it doesn't lock out other |
| 403 | # processes from the disk while it is running. |
| 404 | # If you want to run the fio test for performance purposes, |
| 405 | # take out the ionice and disable hung process detection: |
| 406 | # "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" |
| 407 | # -c 3 = Idle |
| 408 | # Tried lowest priority for "best effort" but still failed |
| 409 | ionice = 'ionice -c 3' |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 410 | options = ['--output-format=json'] |
Gwendal Grignou | 29d2af5 | 2014-05-02 11:32:27 -0700 | [diff] [blame] | 411 | fio_cmd_line = ' '.join([env_vars, ionice, 'fio', |
| 412 | ' '.join(options), |
| 413 | '"' + job + '"']) |
| 414 | fio = utils.run(fio_cmd_line) |
| 415 | |
| 416 | logging.debug(fio.stdout) |
Puthikorn Voravootivat | 1696136 | 2014-05-07 15:57:27 -0700 | [diff] [blame] | 417 | |
| 418 | fio_generate_graph() |
| 419 | |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 420 | filename = re.match('.*FILENAME=(?P<f>[^ ]*)', env_vars).group('f') |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 421 | diskname = utils.get_disk_from_filename(filename) |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 422 | |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 423 | if diskname: |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 424 | model = utils.get_disk_model(diskname) |
| 425 | size = utils.get_disk_size_gb(diskname) |
| 426 | perfdb_name = '%s_%dG' % (model, size) |
| 427 | else: |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 428 | perfdb_name = filename.replace('/', '_') |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 429 | |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 430 | if name_prefix: |
| 431 | perfdb_name = name_prefix + '_' + perfdb_name |
| 432 | |
Gwendal Grignou | 51d5069 | 2014-06-20 11:42:18 -0700 | [diff] [blame] | 433 | result = fio_parser(fio.stdout, prefix=name_prefix) |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 434 | if not graph_prefix: |
| 435 | graph_prefix = '' |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 436 | |
Derek Beckett | e9b63ce | 2020-09-15 13:09:04 -0700 | [diff] [blame^] | 437 | for k, v in six.iteritems(result): |
Gwendal Grignou | 2f16f2f | 2014-06-12 11:28:05 -0700 | [diff] [blame] | 438 | # Remove the prefix for value, and replace it the graph prefix. |
Gwendal Grignou | 51d5069 | 2014-06-20 11:42:18 -0700 | [diff] [blame] | 439 | if name_prefix: |
| 440 | k = k.replace('_' + name_prefix, graph_prefix) |
Puthikorn Voravootivat | 16dc0a2 | 2014-06-24 10:52:04 -0700 | [diff] [blame] | 441 | |
| 442 | # Make graph name to be same as the old code. |
| 443 | if k.endswith('bw'): |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 444 | test.output_perf_value(description=perfdb_name, graph=k, value=v, |
| 445 | units='KB_per_sec', higher_is_better=True) |
David Jimenez | f6b5f51 | 2020-08-20 15:59:23 +1000 | [diff] [blame] | 446 | elif 'clat_percentile_' in k: |
Puthikorn Voravootivat | 8b811e0 | 2014-06-02 14:13:45 -0700 | [diff] [blame] | 447 | test.output_perf_value(description=perfdb_name, graph=k, value=v, |
| 448 | units='us', higher_is_better=False) |
David Jimenez | f6b5f51 | 2020-08-20 15:59:23 +1000 | [diff] [blame] | 449 | elif 'clat_ns_percentile_' in k: |
Alexis Savery | 959cc6d | 2018-08-13 11:39:31 -0700 | [diff] [blame] | 450 | test.output_perf_value(description=perfdb_name, graph=k, value=v, |
| 451 | units='ns', higher_is_better=False) |
Puthikorn Voravootivat | 425b1a7 | 2014-05-14 17:33:27 -0700 | [diff] [blame] | 452 | return result |