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"""
15 import yardstick.ssh as ssh
17 from yardstick.benchmark.scenarios import base
19 LOG = logging.getLogger(__name__)
22 class CACHEstat(base.Scenario):
23 '''Collect cache statistics.
25 This scenario reads system cache hit/miss ratio and other statistics on
28 cache statistics are read using 'cachestat'.
29 cachestat - show Linux page cache hit/miss statistics.
32 This is a proof of concept using Linux ftrace capabilities on older
33 kernels, and works by using function profiling for in-kernel counters.
34 Specifically, four kernel functions are traced:
36 mark_page_accessed() for measuring cache accesses
37 mark_buffer_dirty() for measuring cache writes
38 add_to_page_cache_lru() for measuring page additions
39 account_page_dirtied() for measuring page dirties
41 It is possible that these functions have been renamed (or are different
42 logically) for your kernel version, and this script will not work as-is.
43 This script was written on Linux 3.13. This script is a sandcastle: the
44 kernel may wash some away, and you'll need to rebuild.
46 USAGE: cachestat [-Dht] [interval]
48 cachestat 5 # show stats every 5 seconds
50 Run "cachestat -h" for full usage.
52 WARNING: This uses dynamic tracing of kernel functions, and could cause
53 kernel panics or freezes. Test, and know what you are doing, before use.
54 It also traces cache activity, which can be frequent, and cost some
55 overhead. The statistics should be treated as best-effort: there may be
56 some error margin depending on unusual workload types.
58 REQUIREMENTS: CONFIG_FUNCTION_PROFILER, awk.
60 __scenario_type__ = "CACHEstat"
62 TARGET_SCRIPT = "cache_stat.bash"
64 def __init__(self, scenario_cfg, context_cfg):
65 """Scenario construction."""
66 self.scenario_cfg = scenario_cfg
67 self.context_cfg = context_cfg
68 self.setup_done = False
72 self.target_script = pkg_resources.resource_filename(
73 "yardstick.benchmark.scenarios.compute",
74 CACHEstat.TARGET_SCRIPT)
76 host = self.context_cfg['host']
77 user = host.get('user', 'ubuntu')
78 ip = host.get('ip', None)
79 key_filename = host.get('key_filename', '~/.ssh/id_rsa')
81 LOG.info("user:%s, host:%s", user, ip)
82 self.client = ssh.SSH(user, ip, key_filename=key_filename)
83 self.client.wait(timeout=600)
85 # copy scripts to host
86 self.client.run("cat > ~/cache_stat.sh",
87 stdin=open(self.target_script, 'rb'))
89 self.setup_done = True
91 def _execute_command(self, cmd):
92 """Execute a command on server."""
93 LOG.info("Executing: %s" % cmd)
94 status, stdout, stderr = self.client.execute(cmd)
96 raise RuntimeError("Failed executing command: ",
100 def _filtrate_result(self, result):
103 data_marker = re.compile("\d+")
105 average = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
106 'BUFFERS_MB': 0, 'CACHE_MB': 0}
107 maximum = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
108 'BUFFERS_MB': 0, 'CACHE_MB': 0}
111 for row in result.split('\n'):
114 if line and line[0] == 'HITS':
117 elif line and re.match(data_marker, line[0]):
118 cache = 'cache' + str(ite)
121 if values and len(values) == len(fields):
122 cachestat[cache] = dict(zip(fields, values))
124 for entry in cachestat:
127 average[item] += int(cachestat[entry][item])
129 average[item] += float(cachestat[entry][item][:-1])
133 if int(cachestat[entry][item]) > maximum[item]:
134 maximum[item] = int(cachestat[entry][item])
136 if float(cachestat[entry][item][:-1]) > maximum[item]:
137 maximum[item] = float(cachestat[entry][item][:-1])
141 average[item] = average[item] / len(cachestat)
143 average[item] = str(average[item] / len(cachestat)) + '%'
145 return {'cachestat': cachestat, 'average': average, 'max': maximum}
147 def _get_cache_usage(self):
148 """Get cache statistics."""
149 options = self.scenario_cfg['options']
150 interval = options.get("interval", 1)
152 cmd = "sudo bash cache_stat.sh %s" % (interval)
154 result = self._execute_command(cmd)
155 filtrated_result = self._filtrate_result(result)
157 return filtrated_result
159 def run(self, result):
160 if not self.setup_done:
163 result.update(self._get_cache_usage())