1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
10 """cache hit/miss ratio and usage statistics"""
12 from __future__ import absolute_import
16 import yardstick.ssh as ssh
18 from yardstick.benchmark.scenarios import base
19 from six.moves import zip
21 LOG = logging.getLogger(__name__)
24 class CACHEstat(base.Scenario):
25 """Collect cache statistics.
27 This scenario reads system cache hit/miss ratio and other statistics on
30 cache statistics are read using 'cachestat'.
31 cachestat - show Linux page cache hit/miss statistics.
34 This is a proof of concept using Linux ftrace capabilities on older
35 kernels, and works by using function profiling for in-kernel counters.
36 Specifically, four kernel functions are traced:
38 mark_page_accessed() for measuring cache accesses
39 mark_buffer_dirty() for measuring cache writes
40 add_to_page_cache_lru() for measuring page additions
41 account_page_dirtied() for measuring page dirties
43 It is possible that these functions have been renamed (or are different
44 logically) for your kernel version, and this script will not work as-is.
45 This script was written on Linux 3.13. This script is a sandcastle: the
46 kernel may wash some away, and you'll need to rebuild.
48 USAGE: cachestat [-Dht] [interval]
50 cachestat 5 # show stats every 5 seconds
52 Run "cachestat -h" for full usage.
54 WARNING: This uses dynamic tracing of kernel functions, and could cause
55 kernel panics or freezes. Test, and know what you are doing, before use.
56 It also traces cache activity, which can be frequent, and cost some
57 overhead. The statistics should be treated as best-effort: there may be
58 some error margin depending on unusual workload types.
60 REQUIREMENTS: CONFIG_FUNCTION_PROFILER, awk.
62 __scenario_type__ = "CACHEstat"
64 TARGET_SCRIPT = "cache_stat.bash"
66 def __init__(self, scenario_cfg, context_cfg):
67 """Scenario construction."""
68 self.scenario_cfg = scenario_cfg
69 self.context_cfg = context_cfg
70 self.setup_done = False
74 self.target_script = pkg_resources.resource_filename(
75 "yardstick.benchmark.scenarios.compute",
76 CACHEstat.TARGET_SCRIPT)
78 host = self.context_cfg['host']
80 self.client = ssh.SSH.from_node(host, defaults={"user": "ubuntu"})
81 self.client.wait(timeout=600)
83 # copy scripts to host
84 self.client._put_file_shell(self.target_script, '~/cache_stat.sh')
86 self.setup_done = True
88 def _execute_command(self, cmd):
89 """Execute a command on server."""
90 LOG.info("Executing: %s", cmd)
91 status, stdout, stderr = self.client.execute(cmd)
93 raise RuntimeError("Failed executing command: ",
97 def _filtrate_result(self, result):
100 data_marker = re.compile(r"\d+")
102 average = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
103 'BUFFERS_MB': 0, 'CACHE_MB': 0}
104 maximum = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
105 'BUFFERS_MB': 0, 'CACHE_MB': 0}
108 for row in result.split('\n'):
111 if line and line[0] == 'HITS':
114 elif line and re.match(data_marker, line[0]):
115 cache = 'cache' + str(ite)
118 if values and len(values) == len(fields):
119 cachestat[cache] = dict(list(zip(fields, values)))
121 for entry in cachestat:
124 average[item] += int(cachestat[entry][item])
126 average[item] += float(cachestat[entry][item][:-1])
130 if int(cachestat[entry][item]) > maximum[item]:
131 maximum[item] = int(cachestat[entry][item])
133 if float(cachestat[entry][item][:-1]) > maximum[item]:
134 maximum[item] = float(cachestat[entry][item][:-1])
138 average[item] = average[item] / len(cachestat)
140 average[item] = str(average[item] / len(cachestat)) + '%'
142 return {'cachestat': cachestat, 'average': average, 'max': maximum}
144 def _get_cache_usage(self):
145 """Get cache statistics."""
146 options = self.scenario_cfg['options']
147 interval = options.get("interval", 1)
149 cmd = "sudo bash cache_stat.sh %s" % (interval)
151 result = self._execute_command(cmd)
152 filtrated_result = self._filtrate_result(result)
154 return filtrated_result
156 def run(self, result):
157 if not self.setup_done:
160 result.update(self._get_cache_usage())