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 ssh_port = host.get("ssh_port", ssh.DEFAULT_PORT)
79 ip = host.get('ip', None)
80 key_filename = host.get('key_filename', '~/.ssh/id_rsa')
82 LOG.info("user:%s, host:%s", user, ip)
83 self.client = ssh.SSH(user, ip, key_filename=key_filename,
85 self.client.wait(timeout=600)
87 # copy scripts to host
88 self.client._put_file_shell(self.target_script, '~/cache_stat.sh')
90 self.setup_done = True
92 def _execute_command(self, cmd):
93 """Execute a command on server."""
94 LOG.info("Executing: %s", cmd)
95 status, stdout, stderr = self.client.execute(cmd)
97 raise RuntimeError("Failed executing command: ",
101 def _filtrate_result(self, result):
104 data_marker = re.compile("\d+")
106 average = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
107 'BUFFERS_MB': 0, 'CACHE_MB': 0}
108 maximum = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
109 'BUFFERS_MB': 0, 'CACHE_MB': 0}
112 for row in result.split('\n'):
115 if line and line[0] == 'HITS':
118 elif line and re.match(data_marker, line[0]):
119 cache = 'cache' + str(ite)
122 if values and len(values) == len(fields):
123 cachestat[cache] = dict(zip(fields, values))
125 for entry in cachestat:
128 average[item] += int(cachestat[entry][item])
130 average[item] += float(cachestat[entry][item][:-1])
134 if int(cachestat[entry][item]) > maximum[item]:
135 maximum[item] = int(cachestat[entry][item])
137 if float(cachestat[entry][item][:-1]) > maximum[item]:
138 maximum[item] = float(cachestat[entry][item][:-1])
142 average[item] = average[item] / len(cachestat)
144 average[item] = str(average[item] / len(cachestat)) + '%'
146 return {'cachestat': cachestat, 'average': average, 'max': maximum}
148 def _get_cache_usage(self):
149 """Get cache statistics."""
150 options = self.scenario_cfg['options']
151 interval = options.get("interval", 1)
153 cmd = "sudo bash cache_stat.sh %s" % (interval)
155 result = self._execute_command(cmd)
156 filtrated_result = self._filtrate_result(result)
158 return filtrated_result
160 def run(self, result):
161 if not self.setup_done:
164 result.update(self._get_cache_usage())