aboutsummaryrefslogtreecommitdiff
path: root/heatmaps/heatmap_generator_test.py
diff options
context:
space:
mode:
authorTiancong Wang <tcwang@google.com>2019-05-06 15:42:43 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-05-17 18:38:15 -0700
commit3f06bf5fa1c53aa42959d351accc8888eb329343 (patch)
tree86be399d8cf24ff607f30a21f8b31634bffe4e4a /heatmaps/heatmap_generator_test.py
parentadb9f7a366cb5e8ae5eb5c860da191e0418e8901 (diff)
downloadtoolchain-utils-3f06bf5fa1c53aa42959d351accc8888eb329343.tar.gz
toolchain-utils: Improve heatmap tool.
The overall goal of this patch is to improve heatmap tool in a way that facilitates the tool to be integrated into crosperf. The patch needs to make sure the heat_map.py is not only able to run as a standalone tool, but also is compatible to crosperf. It also adds functionality to the heatmap that when the address range of huge page (offset within the text section) is provided, the heatmap displays symbols within and outside of the region with different colors. This patch adds following improvements: 1. It adds support in heat_map.py that skips copying perf.data into the chroot again, if the provided path to perf.data is within a chroot. 2. It adds support in heatmap_generator.py to take in a range of huge page as (start, end). Update corresponding unit test. 3. It adds extensive unit tests for heat_map.py. The unit test tests both each functions and also makes sure it run as a standalone tool. BUG=chromium:956109 TEST=Tested locally and pass unit tests. Change-Id: I8008da51cceb0ad90df29042faf7a9d91d04f4b1 Reviewed-on: https://chromium-review.googlesource.com/1582523 Commit-Ready: Tiancong Wang <tcwang@google.com> Tested-by: Tiancong Wang <tcwang@google.com> Reviewed-by: Tiancong Wang <tcwang@google.com>
Diffstat (limited to 'heatmaps/heatmap_generator_test.py')
-rwxr-xr-xheatmaps/heatmap_generator_test.py244
1 files changed, 244 insertions, 0 deletions
diff --git a/heatmaps/heatmap_generator_test.py b/heatmaps/heatmap_generator_test.py
new file mode 100755
index 00000000..271f9f42
--- /dev/null
+++ b/heatmaps/heatmap_generator_test.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for heatmap_generator.py."""
+
+from __future__ import division, print_function
+
+import os
+import unittest
+
+import heatmap_generator
+
+
+def _write_perf_mmap(pid, tid, addr, size, fp):
+ print(
+ '0 0 0 0 PERF_RECORD_MMAP2 %d/%d: '
+ '[%x(%x) @ 0x0 0:0 0 0] '
+ 'r-xp /opt/google/chrome/chrome\n' % (pid, tid, addr, size),
+ file=fp)
+
+
+def _write_perf_fork(pid_from, tid_from, pid_to, tid_to, fp):
+ print(
+ '0 0 0 0 PERF_RECORD_FORK(%d:%d):(%d:%d)\n' % (pid_to, tid_to, pid_from,
+ tid_from),
+ file=fp)
+
+
+def _write_perf_exit(pid_from, tid_from, pid_to, tid_to, fp):
+ print(
+ '0 0 0 0 PERF_RECORD_EXIT(%d:%d):(%d:%d)\n' % (pid_to, tid_to, pid_from,
+ tid_from),
+ file=fp)
+
+
+def _write_perf_sample(pid, tid, addr, fp):
+ print(
+ '0 0 0 0 PERF_RECORD_SAMPLE(IP, 0x2): '
+ '%d/%d: %x period: 100000 addr: 0' % (pid, tid, addr),
+ file=fp)
+ print(' ... thread: chrome:%d' % tid, file=fp)
+ print(' ...... dso: /opt/google/chrome/chrome\n', file=fp)
+
+
+def _heatmap(file_name, page_size=4096, hugepage=None):
+ generator = heatmap_generator.HeatmapGenerator(
+ file_name, page_size, hugepage, '',
+ log_level='none') # Don't log to stdout
+ generator.draw()
+
+
+def _cleanup(file_name):
+ files = [
+ file_name, 'out.txt', 'inst-histo.txt', 'inst-histo-hp.txt',
+ 'inst-histo-sp.txt', 'heat_map.png', 'timeline.png'
+ ]
+ for f in files:
+ if os.path.exists(f):
+ os.remove(f)
+
+
+class Tests(unittest.TestCase):
+ """All of our tests for heatmap_generator."""
+
+ def test_with_one_mmap_one_sample(self):
+ """Tests one perf record and one sample."""
+ fname = 'test.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x100, f)
+ _write_perf_sample(101, 101, 0xABCD101, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname)
+ self.assertIn('out.txt', os.listdir('.'))
+ with open('out.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 1)
+ self.assertIn('101/101: 1 0', lines[0])
+
+ def test_with_one_mmap_multiple_samples(self):
+ """Tests one perf record and three samples."""
+ fname = 'test.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x100, f)
+ _write_perf_sample(101, 101, 0xABCD101, f)
+ _write_perf_sample(101, 101, 0xABCD102, f)
+ _write_perf_sample(101, 101, 0xABCE102, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname)
+ self.assertIn('out.txt', os.listdir('.'))
+ with open('out.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 3)
+ self.assertIn('101/101: 1 0', lines[0])
+ self.assertIn('101/101: 2 0', lines[1])
+ self.assertIn('101/101: 3 4096', lines[2])
+
+ def test_with_fork_and_exit(self):
+ """Tests perf fork and perf exit."""
+ fname = 'test_fork.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x100, f)
+ _write_perf_fork(101, 101, 202, 202, f)
+ _write_perf_sample(101, 101, 0xABCD101, f)
+ _write_perf_sample(202, 202, 0xABCE101, f)
+ _write_perf_exit(202, 202, 202, 202, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname)
+ self.assertIn('out.txt', os.listdir('.'))
+ with open('out.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 2)
+ self.assertIn('101/101: 1 0', lines[0])
+ self.assertIn('202/202: 2 4096', lines[1])
+
+ def test_hugepage_creates_two_chrome_mmaps(self):
+ """Test two chrome mmaps for the same process."""
+ fname = 'test_hugepage.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x1000, f)
+ _write_perf_fork(101, 101, 202, 202, f)
+ _write_perf_mmap(202, 202, 0xABCD000, 0x100, f)
+ _write_perf_mmap(202, 202, 0xABCD300, 0xD00, f)
+ _write_perf_sample(101, 101, 0xABCD102, f)
+ _write_perf_sample(202, 202, 0xABCD102, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname)
+ self.assertIn('out.txt', os.listdir('.'))
+ with open('out.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 2)
+ self.assertIn('101/101: 1 0', lines[0])
+ self.assertIn('202/202: 2 0', lines[1])
+
+ def test_hugepage_creates_two_chrome_mmaps_fail(self):
+ """Test two chrome mmaps for the same process."""
+ fname = 'test_hugepage.txt'
+ # Cases where first_mmap.size < second_mmap.size
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x1000, f)
+ _write_perf_fork(101, 101, 202, 202, f)
+ _write_perf_mmap(202, 202, 0xABCD000, 0x10000, f)
+ self.addCleanup(_cleanup, fname)
+ with self.assertRaises(AssertionError) as msg:
+ _heatmap(fname)
+ self.assertIn('Original MMAP size', str(msg.exception))
+
+ # Cases where first_mmap.address > second_mmap.address
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x1000, f)
+ _write_perf_fork(101, 101, 202, 202, f)
+ _write_perf_mmap(202, 202, 0xABCC000, 0x10000, f)
+ with self.assertRaises(AssertionError) as msg:
+ _heatmap(fname)
+ self.assertIn('Original MMAP starting address', str(msg.exception))
+
+ # Cases where first_mmap.address + size <
+ # second_mmap.address + second_mmap.size
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x1000, f)
+ _write_perf_fork(101, 101, 202, 202, f)
+ _write_perf_mmap(202, 202, 0xABCD100, 0x10000, f)
+ with self.assertRaises(AssertionError) as msg:
+ _heatmap(fname)
+ self.assertIn('exceeds the end of original MMAP', str(msg.exception))
+
+ def test_histogram(self):
+ """Tests if the tool can generate correct histogram.
+
+ In the tool, histogram is generated from statistics
+ of perf samples (saved to out.txt). The histogram is
+ generated by perf-to-inst-page.sh and saved to
+ inst-histo.txt. It will be used to draw heat maps.
+ """
+ fname = 'test_histo.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x100, f)
+ for i in range(100):
+ _write_perf_sample(101, 101, 0xABCD000 + i, f)
+ _write_perf_sample(101, 101, 0xABCE000 + i, f)
+ _write_perf_sample(101, 101, 0xABFD000 + i, f)
+ _write_perf_sample(101, 101, 0xAFCD000 + i, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname)
+ self.assertIn('inst-histo.txt', os.listdir('.'))
+ with open('inst-histo.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 4)
+ self.assertIn('100 0', lines[0])
+ self.assertIn('100 4096', lines[1])
+ self.assertIn('100 196608', lines[2])
+ self.assertIn('100 4194304', lines[3])
+
+ def test_histogram_two_mb_page(self):
+ """Tests handling of 2MB page."""
+ fname = 'test_histo.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x100, f)
+ for i in range(100):
+ _write_perf_sample(101, 101, 0xABCD000 + i, f)
+ _write_perf_sample(101, 101, 0xABCE000 + i, f)
+ _write_perf_sample(101, 101, 0xABFD000 + i, f)
+ _write_perf_sample(101, 101, 0xAFCD000 + i, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname, page_size=2 * 1024 * 1024)
+ self.assertIn('inst-histo.txt', os.listdir('.'))
+ with open('inst-histo.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 2)
+ self.assertIn('300 0', lines[0])
+ self.assertIn('100 4194304', lines[1])
+
+ def test_histogram_in_and_out_hugepage(self):
+ """Tests handling the case of separating samples in and out huge page."""
+ fname = 'test_histo.txt'
+ with open(fname, 'w') as f:
+ _write_perf_mmap(101, 101, 0xABCD000, 0x100, f)
+ for i in range(100):
+ _write_perf_sample(101, 101, 0xABCD000 + i, f)
+ _write_perf_sample(101, 101, 0xABCE000 + i, f)
+ _write_perf_sample(101, 101, 0xABFD000 + i, f)
+ _write_perf_sample(101, 101, 0xAFCD000 + i, f)
+ self.addCleanup(_cleanup, fname)
+ _heatmap(fname, hugepage=[0, 8192])
+ file_list = os.listdir('.')
+ self.assertNotIn('inst-histo.txt', file_list)
+ self.assertIn('inst-histo-hp.txt', file_list)
+ self.assertIn('inst-histo-sp.txt', file_list)
+ with open('inst-histo-hp.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 2)
+ self.assertIn('100 0', lines[0])
+ self.assertIn('100 4096', lines[1])
+ with open('inst-histo-sp.txt') as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 2)
+ self.assertIn('100 196608', lines[0])
+ self.assertIn('100 4194304', lines[1])
+
+
+if __name__ == '__main__':
+ unittest.main()