1#!/usr/bin/env python3
2#
3# Copyright 2019, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""
19Unit tests for inode2filename module.
20
21Install:
22  $> sudo apt-get install python3-pytest   ##  OR
23  $> pip install -U pytest
24See also https://docs.pytest.org/en/latest/getting-started.html
25
26Usage:
27  $> ./inode2filename_test.py
28  $> pytest inode2filename_test.py
29  $> python -m pytest inode2filename_test.py
30
31See also https://docs.pytest.org/en/latest/usage.html
32"""
33
34# global imports
35import io
36from copy import deepcopy
37
38# pip imports
39# local imports
40from trace2db import *
41
42# This pretty-prints the raw dictionary of the sqlalchemy object if it fails.
43class EqualsSqlAlchemyObject:
44  # For convenience to write shorter tests, we also add 'ignore_fields' which allow us to specify
45  # which fields to ignore when doing the comparison.
46  def __init__(self_, self, ignore_fields=[]):
47    self_.self = self
48    self_.ignore_fields = ignore_fields
49
50  # Do field-by-field comparison.
51  # It seems that SQLAlchemy does not implement __eq__ itself so we have to do it ourselves.
52  def __eq__(self_, other):
53    if isinstance(other, EqualsSqlAlchemyObject):
54      other = other.self
55
56    self = self_.self
57
58    classes_match = isinstance(other, self.__class__)
59    a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
60
61    #compare based on equality our attributes, ignoring SQLAlchemy internal stuff
62
63    a.pop('_sa_instance_state', None)
64    b.pop('_sa_instance_state', None)
65
66    for f in self_.ignore_fields:
67      a.pop(f, None)
68      b.pop(f, None)
69
70    attrs_match = (a == b)
71    return classes_match and attrs_match
72
73  def __repr__(self):
74    return repr(self.self.__dict__)
75
76
77def assert_eq_ignore_id(left, right):
78  # This pretty-prints the raw dictionary of the sqlalchemy object if it fails.
79  # It does field-by-field comparison, but ignores the 'id' field.
80  assert EqualsSqlAlchemyObject(left, ignore_fields=['id']) == EqualsSqlAlchemyObject(right)
81
82def parse_trace_file_to_db(*contents):
83  """
84  Make temporary in-memory sqlite3 database by parsing the string contents as a trace.
85
86  :return: Trace2Db instance
87  """
88  buf = io.StringIO()
89
90  for c in contents:
91    buf.write(c)
92    buf.write("\n")
93
94  buf.seek(0)
95
96  t2d = Trace2Db(":memory:")
97  t2d.parse_file_buf_into_db(buf)
98
99  buf.close()
100
101  return t2d
102
103def test_ftrace_mm_filemap_add_to_pagecache():
104  test_contents = """
105MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
106MediaStoreImpor-27212 (27176) [000] .... 16136.595920: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000048e2e156 pfn=677645 ofs=126976
107MediaStoreImpor-27212 (27176) [000] .... 16136.597793: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000051eabfb2 pfn=677644 ofs=122880
108MediaStoreImpor-27212 (27176) [000] .... 16136.597815: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000ce7cd606 pfn=677643 ofs=131072
109MediaStoreImpor-27212 (27176) [000] .... 16136.603732: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=000000008ffd3030 pfn=730119 ofs=186482688
110MediaStoreImpor-27212 (27176) [000] .... 16136.604126: mm_filemap_add_to_page_cache: dev 253:6 ino b1d8 page=0000000098d4d2e2 pfn=829676 ofs=0
111          <...>-27197 (-----) [002] .... 16136.613471: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000aca88a97 pfn=743346 ofs=241664
112          <...>-27197 (-----) [002] .... 16136.615979: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000351f2bc1 pfn=777799 ofs=106496
113          <...>-27224 (-----) [006] .... 16137.400090: mm_filemap_add_to_page_cache: dev 253:6 ino 712d page=000000006ff7ffdb pfn=754861 ofs=0
114          <...>-1396  (-----) [000] .... 16137.451660: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000ba0cbb34 pfn=769173 ofs=187191296
115          <...>-1396  (-----) [000] .... 16137.453020: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000f6ef038e pfn=820291 ofs=0
116          <...>-1396  (-----) [000] .... 16137.453067: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=0000000083ebc446 pfn=956463 ofs=4096
117          <...>-1396  (-----) [000] .... 16137.453101: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000009dc2cd25 pfn=822813 ofs=8192
118          <...>-1396  (-----) [000] .... 16137.453113: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000a11167fb pfn=928650 ofs=12288
119          <...>-1396  (-----) [000] .... 16137.453126: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000c1c3311b pfn=621110 ofs=16384
120          <...>-1396  (-----) [000] .... 16137.453139: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000009aa78342 pfn=689370 ofs=20480
121          <...>-1396  (-----) [000] .... 16137.453151: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=0000000082cddcd6 pfn=755584 ofs=24576
122          <...>-1396  (-----) [000] .... 16137.453162: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000b0249bc7 pfn=691431 ofs=28672
123          <...>-1396  (-----) [000] .... 16137.453183: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000006a776ff0 pfn=795084 ofs=32768
124          <...>-1396  (-----) [000] .... 16137.453203: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000001a4918a7 pfn=806998 ofs=36864
125          <...>-2578  (-----) [002] .... 16137.561871: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000d65af9d2 pfn=719246 ofs=187015168
126          <...>-2578  (-----) [002] .... 16137.562846: mm_filemap_add_to_page_cache: dev 253:6 ino b25a page=000000002f6ba74f pfn=864982 ofs=0
127          <...>-2578  (-----) [000] .... 16138.104500: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000f888d0f6 pfn=805812 ofs=192794624
128          <...>-2578  (-----) [000] .... 16138.105836: mm_filemap_add_to_page_cache: dev 253:6 ino b7dd page=000000003749523b pfn=977196 ofs=0
129          <...>-27215 (-----) [001] .... 16138.256881: mm_filemap_add_to_page_cache: dev 253:6 ino 758f page=000000001b375de1 pfn=755928 ofs=0
130          <...>-27215 (-----) [001] .... 16138.257526: mm_filemap_add_to_page_cache: dev 253:6 ino 7591 page=000000004e039481 pfn=841534 ofs=0
131 NonUserFacing6-5246  ( 1322) [005] .... 16138.356491: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000d65af9d2 pfn=719246 ofs=161890304
132 NonUserFacing6-5246  ( 1322) [005] .... 16138.357538: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000002f6ba74f pfn=864982 ofs=0
133 NonUserFacing6-5246  ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
134          <...>-27197 (-----) [005] .... 16140.143224: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000a42527c6 pfn=1076669 ofs=32768
135  """
136
137  t2d = parse_trace_file_to_db(test_contents)
138  session = t2d.session
139
140  first_row = session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).first()
141
142  #dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
143  assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
144      ino=0x7580, page=0x0000000060e990c7, pfn=677646, ofs=159744), first_row)
145
146  second_to_last_row = session.query(MmFilemapAddToPageCache).filter(MmFilemapAddToPageCache.page.in_([0x000000006e0f8322])).first()
147
148  # dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
149  assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
150      ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row)
151
152def test_systrace_mm_filemap_add_to_pagecache():
153  test_contents = """
154<!DOCTYPE html>
155<html>
156<head i18n-values="dir:textdirection;">
157<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
158<meta charset="utf-8"/>
159<title>Android System Trace</title>
160  <script class="trace-data" type="application/text">
161PROCESS DUMP
162USER           PID  PPID     VSZ    RSS WCHAN  PC S NAME                        COMM
163root             1     0   62148   5976 0       0 S init                        [init]
164root             2     0       0      0 0       0 S [kthreadd]                  [kthreadd]
165  </script>
166
167  <script class="trace-data" type="application/text">
168MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
169NonUserFacing6-5246  ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
170  </script>
171
172  <script class="trace-data" type="application/text">
173{"traceEvents": [{"category": "process_argv", "name": "process_argv", "args": {"argv": ["/mnt/ssd3/workspace/master/external/chromium-trace/systrace.py", "-t", "5", "pagecache"]}, "pid": 160383, "ts": 1037300940509.7991, "tid": 139628672526080, "ph": "M"}, {"category": "python", "name": "clock_sync", "args": {"issue_ts": 1037307346185.212, "sync_id": "9a7e4fe3-89ad-441f-8226-8fe533fe973e"}, "pid": 160383, "ts": 1037307351643.906, "tid": 139628726089536, "ph": "c"}], "metadata": {"clock-domain": "SYSTRACE"}}
174  </script>
175<!-- END TRACE -->
176  """
177
178  t2d = parse_trace_file_to_db(test_contents)
179  session = t2d.session
180
181  first_row = session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).first()
182
183  #dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
184  assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
185      ino=0x7580, page=0x0000000060e990c7, pfn=677646, ofs=159744), first_row)
186
187  second_to_last_row = session.query(MmFilemapAddToPageCache).filter(MmFilemapAddToPageCache.page.in_([0x000000006e0f8322])).first()
188
189  # dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
190  assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
191      ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row)
192
193def test_timestamp_filter():
194  test_contents = """
195    MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
196    NonUserFacing6-5246  ( 1322) [005] .... 16139.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
197    MediaStoreImpor-27212 (27176) [000] .... 16136.604126: mm_filemap_add_to_page_cache: dev 253:6 ino b1d8 page=0000000098d4d2e2 pfn=829676 ofs=0
198  """
199
200  t2d = parse_trace_file_to_db(test_contents)
201  session = t2d.session
202
203  end_time = 16137.0
204
205  results = session.query(MmFilemapAddToPageCache).join(
206      MmFilemapAddToPageCache.raw_ftrace_entry).filter(
207      RawFtraceEntry.timestamp <= end_time).order_by(
208      MmFilemapAddToPageCache.id).all()
209
210  assert len(results) == 2
211  assert_eq_ignore_id(
212      MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
213                              ino=0x7580, page=0x0000000060e990c7, pfn=677646,
214                              ofs=159744), results[0])
215  assert_eq_ignore_id(
216      MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
217                              ino=0xb1d8, page=0x0000000098d4d2e2, pfn=829676,
218                              ofs=0), results[1])
219
220
221if __name__ == '__main__':
222  pytest.main()
223