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']
79 user = host.get('user', 'ubuntu')
80 ssh_port = host.get("ssh_port", ssh.DEFAULT_PORT)
81 ip = host.get('ip', None)
82 key_filename = host.get('key_filename', '~/.ssh/id_rsa')
84 LOG.info("user:%s, host:%s", user, ip)
85 self.client = ssh.SSH(user, ip, key_filename=key_filename,
87 self.client.wait(timeout=600)
89 # copy scripts to host
90 self.client._put_file_shell(self.target_script, '~/cache_stat.sh')
92 self.setup_done = True
94 def _execute_command(self, cmd):
95 """Execute a command on server."""
96 LOG.info("Executing: %s", cmd)
97 status, stdout, stderr = self.client.execute(cmd)
99 raise RuntimeError("Failed executing command: ",
103 def _filtrate_result(self, result):
106 data_marker = re.compile("\d+")
108 average = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
109 'BUFFERS_MB': 0, 'CACHE_MB': 0}
110 maximum = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
111 'BUFFERS_MB': 0, 'CACHE_MB': 0}
114 for row in result.split('\n'):
117 if line and line[0] == 'HITS':
120 elif line and re.match(data_marker, line[0]):
121 cache = 'cache' + str(ite)
124 if values and len(values) == len(fields):
125 cachestat[cache] = dict(list(zip(fields, values)))
127 for entry in cachestat:
130 average[item] += int(cachestat[entry][item])
132 average[item] += float(cachestat[entry][item][:-1])
136 if int(cachestat[entry][item]) > maximum[item]:
137 maximum[item] = int(cachestat[entry][item])
139 if float(cachestat[entry][item][:-1]) > maximum[item]:
140 maximum[item] = float(cachestat[entry][item][:-1])
144 average[item] = average[item] / len(cachestat)
146 average[item] = str(average[item] / len(cachestat)) + '%'
148 return {'cachestat': cachestat, 'average': average, 'max': maximum}
150 def _get_cache_usage(self):
151 """Get cache statistics."""
152 options = self.scenario_cfg['options']
153 interval = options.get("interval", 1)
155 cmd = "sudo bash cache_stat.sh %s" % (interval)
157 result = self._execute_command(cmd)
158 filtrated_result = self._filtrate_result(result)
160 return filtrated_result
162 def run(self, result):
163 if not self.setup_done:
166 result.update(self._get_cache_usage())