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.run("cat > ~/cache_stat.sh",
89 stdin=open(self.target_script, 'rb'))
91 self.setup_done = True
93 def _execute_command(self, cmd):
94 """Execute a command on server."""
95 LOG.info("Executing: %s" % cmd)
96 status, stdout, stderr = self.client.execute(cmd)
98 raise RuntimeError("Failed executing command: ",
102 def _filtrate_result(self, result):
105 data_marker = re.compile("\d+")
107 average = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
108 'BUFFERS_MB': 0, 'CACHE_MB': 0}
109 maximum = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
110 'BUFFERS_MB': 0, 'CACHE_MB': 0}
113 for row in result.split('\n'):
116 if line and line[0] == 'HITS':
119 elif line and re.match(data_marker, line[0]):
120 cache = 'cache' + str(ite)
123 if values and len(values) == len(fields):
124 cachestat[cache] = dict(zip(fields, values))
126 for entry in cachestat:
129 average[item] += int(cachestat[entry][item])
131 average[item] += float(cachestat[entry][item][:-1])
135 if int(cachestat[entry][item]) > maximum[item]:
136 maximum[item] = int(cachestat[entry][item])
138 if float(cachestat[entry][item][:-1]) > maximum[item]:
139 maximum[item] = float(cachestat[entry][item][:-1])
143 average[item] = average[item] / len(cachestat)
145 average[item] = str(average[item] / len(cachestat)) + '%'
147 return {'cachestat': cachestat, 'average': average, 'max': maximum}
149 def _get_cache_usage(self):
150 """Get cache statistics."""
151 options = self.scenario_cfg['options']
152 interval = options.get("interval", 1)
154 cmd = "sudo bash cache_stat.sh %s" % (interval)
156 result = self._execute_command(cmd)
157 filtrated_result = self._filtrate_result(result)
159 return filtrated_result
161 def run(self, result):
162 if not self.setup_done:
165 result.update(self._get_cache_usage())