blob: 10ee5a867e9442e6f840b47ede6896ae45d15ac9 [file]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from analysis.analysis_testcase import AnalysisTestCase
from analysis.callstack_detectors import StartOfCallStack
from analysis.stacktrace import FunctionLine
from analysis.stacktrace import CallStack
from analysis.stacktrace import CallStackBuffer
from analysis.stacktrace import ProfilerStackFrame
from analysis.stacktrace import StackFrame
from analysis.stacktrace import Stacktrace
from analysis.stacktrace import StacktraceBuffer
from analysis.type_enums import CallStackFormatType
from analysis.type_enums import LanguageType
from libs.deps.dependency import Dependency
class ProfilerStackFrameTest(AnalysisTestCase):
def testParseFrameWithMissingFields(self):
"""Tests parsing a frame with the optional fields missing."""
frame_dict = {
'difference': 0.01,
'log_change_factor': -8.1,
'responsible': False,
# other fields are absent e.g. filename
}
deps = {'chrome': Dependency('chrome', 'https://repo', '1')}
frame, language_type = ProfilerStackFrame.Parse(frame_dict, 0, deps)
self.assertEqual(frame.index, 0)
self.assertEqual(frame.difference, 0.01)
self.assertEqual(frame.log_change_factor, -8.1)
self.assertEqual(frame.responsible, False)
self.assertIsNone(frame.dep_path)
self.assertIsNone(frame.function)
self.assertIsNone(frame.file_path)
self.assertIsNone(frame.raw_file_path)
self.assertIsNone(frame.repo_url)
self.assertIsNone(frame.function_start_line)
self.assertIsNone(frame.lines_old)
self.assertIsNone(frame.lines_new)
self.assertEqual(language_type, LanguageType.CPP)
def testParseFrame(self):
"""Tests successfully parsing a stacktrace with one frame."""
frame_dict = {
'difference': 0.01,
'log_change_factor': -8.1,
'responsible': False,
'filename': 'chrome/app/chrome_exe_main_win.cc',
'function_name': 'wWinMain',
'function_start_line': 484,
'lines': [
[
{'line': 490, 'sample_fraction': 0.7},
{'line': 511, 'sample_fraction': 0.3}
],
[
{'line': 490, 'sample_fraction': 0.9},
{'line': 511, 'sample_fraction': 0.1}
]
]
}
deps = {'chrome': Dependency('chrome', 'https://repo', '1')}
frame, language_type = ProfilerStackFrame.Parse(frame_dict, 1, deps)
self.assertEqual(frame.index, 1)
self.assertEqual(frame.difference, 0.01)
self.assertEqual(frame.log_change_factor, -8.1)
self.assertEqual(frame.responsible, False)
self.assertEqual(frame.dep_path, 'chrome')
self.assertEqual(frame.function, 'wWinMain')
self.assertEqual(frame.file_path, 'app/chrome_exe_main_win.cc')
self.assertEqual(frame.raw_file_path, 'chrome/app/chrome_exe_main_win.cc')
self.assertEqual(frame.repo_url, 'https://repo')
self.assertEqual(frame.function_start_line, 484)
expected_lines_old = (
FunctionLine(line=490, sample_fraction=0.7),
FunctionLine(line=511, sample_fraction=0.3),
)
expected_lines_new = (
FunctionLine(line=490, sample_fraction=0.9),
FunctionLine(line=511, sample_fraction=0.1),
)
self.assertEqual(frame.lines_old, expected_lines_old)
self.assertEqual(frame.lines_new, expected_lines_new)
self.assertEqual(language_type, LanguageType.CPP)
def testCrashedLineNumbersForProfilerStackFrame(self):
"""Tests the ``crashed_line_numbers`` property."""
frame = ProfilerStackFrame(
0, 0, 0, False, function_start_line=5,
lines_old=(
FunctionLine(line=10, sample_fraction=0.3),
FunctionLine(line=15, sample_fraction=0.7)
),
lines_new=(
FunctionLine(line=12, sample_fraction=0.5),
FunctionLine(line=19, sample_fraction=0.5)
))
self.assertEqual(frame.crashed_line_numbers, (5, 12, 19))
frame2 = ProfilerStackFrame(0, 0, 0, False, function_start_line=10)
self.assertEqual(frame2.crashed_line_numbers, (10,))
frame3 = ProfilerStackFrame(0, 0, 0, False)
self.assertIsNone(frame3.crashed_line_numbers)
def testBlameUrlForProfilerStackFrame(self):
"""Tests that ``ProfilerStackFrame.BlameUrl`` generates the correct url."""
frame = ProfilerStackFrame(0, 0, float('inf'), False, 'src', 'func',
'f.cc', 'src/f.cc')
self.assertEqual(frame.BlameUrl('1'), None)
frame = frame._replace(repo_url = 'https://repo_url')
self.assertEqual(frame.BlameUrl('1'), 'https://repo_url/+blame/1/f.cc')
def testFailureWhenProfilerStackFrameIndexIsNone(self):
"""Tests that a TypeError is raised when the ``index`` is ``None``."""
with self.assertRaises(TypeError):
ProfilerStackFrame(None, 0, float('inf'), False, 'src', 'func',
'f.cc', 'src/f.cc')
class CallStackTest(AnalysisTestCase):
def testCallStackBool(self):
self.assertFalse(CallStack(0, [], None, None))
frame = StackFrame(0, 'src', 'func', 'f.cc', 'src/f.cc', [])
self.assertTrue(CallStack(0, [frame], None, None))
def testStackFrameToString(self):
self.assertEqual(
StackFrame(0, 'src', 'func', 'f.cc', 'src/f.cc', []).ToString(),
'#0 0xXXX in func src/f.cc')
self.assertEqual(
StackFrame(0, 'src', 'func', 'f.cc', 'src/f.cc', [1]).ToString(),
'#0 0xXXX in func src/f.cc:1')
self.assertEqual(
StackFrame(0, 'src', 'func', 'f.cc', 'src/f.cc', [1, 2]).ToString(),
'#0 0xXXX in func src/f.cc:1:1')
def testBlameUrlForStackFrame(self):
frame = StackFrame(0, 'src', 'func', 'f.cc', 'src/f.cc', [])
self.assertEqual(frame.BlameUrl('1'), None)
frame = frame._replace(repo_url = 'https://repo_url')
self.assertEqual(frame.BlameUrl('1'), 'https://repo_url/+blame/1/f.cc')
frame = frame._replace(crashed_line_numbers = [9, 10])
self.assertEqual(frame.BlameUrl('1'), 'https://repo_url/+blame/1/f.cc#9')
def testCallStackConstructorIsLanguageJavaIfFormatJava(self):
self.assertEqual(
CallStack(0, format_type=CallStackFormatType.JAVA).language_type,
LanguageType.JAVA)
def testParseStackFrameForJavaCallstackFormat(self):
language_type = None
format_type = CallStackFormatType.JAVA
self.assertIsNone(
StackFrame.Parse(language_type, format_type, 'dummy line', {}))
deps = {'org': Dependency('org', 'https://repo', '1')}
frame = StackFrame.Parse(language_type, format_type,
' at org.a.b(a.java:609)', deps)
self._VerifyTwoStackFramesEqual(
frame,
StackFrame(0, 'org', 'org.a.b', 'a.java', 'org/a.java', [609]))
def testParseStackFrameForSyzyasanCallstackFormat(self):
language_type = None
format_type = CallStackFormatType.SYZYASAN
self.assertIsNone(
StackFrame.Parse(language_type, format_type, 'dummy line', {}))
deps = {'src/content': Dependency('src/content', 'https://repo', '1')}
frame = StackFrame.Parse(language_type, format_type,
'c::p::n [src/content/e.cc @ 165]', deps)
self._VerifyTwoStackFramesEqual(
frame,
StackFrame(
0, 'src/content', 'c::p::n', 'e.cc', 'src/content/e.cc', [165]))
def testParseStackFrameForDefaultCallstackFormat(self):
language_type = None
format_type = CallStackFormatType.DEFAULT
self.assertIsNone(
StackFrame.Parse(language_type, format_type, 'dummy line', {}))
deps = {'tp/webrtc': Dependency('tp/webrtc', 'https://repo', '1')}
frame = StackFrame.Parse(language_type, format_type,
'#0 0x52617a in func0 tp/webrtc/a.c:38:3', deps)
self._VerifyTwoStackFramesEqual(
frame,
StackFrame(
0, 'tp/webrtc', 'func0', 'a.c', 'tp/webrtc/a.c', [38, 39, 40, 41]))
frame = StackFrame.Parse(language_type, format_type,
'#1 0x526 in func::func2::func3 tp/webrtc/a.c:3:2', deps)
self._VerifyTwoStackFramesEqual(
frame,
StackFrame(
1, 'tp/webrtc', 'func::func2::func3', 'a.c', 'tp/webrtc/a.c',
[3, 4, 5]))
def testParseStackFrameForFracasJavaStack(self):
format_type = CallStackFormatType.DEFAULT
language_type = LanguageType.JAVA
frame = StackFrame.Parse(language_type, format_type,
'#0 0xxx in android.app.func app.java:2450', {})
self._VerifyTwoStackFramesEqual(
frame,
StackFrame(
0, '', 'android.app.func', 'android/app.java',
'android/app.java', [2450]))
class CallStackBufferTest(AnalysisTestCase):
def setUp(self):
super(CallStackBufferTest, self).setUp()
self.stack_buffer = CallStackBuffer(0, frame_list=[
StackFrame(0, 'repo/path1', 'func1', 'a/c/f1.cc', 'a/b/f1.cc',
[1, 2], 'https://repo1'),
StackFrame(1, 'repo/path2', 'func2', 'a/c/f2.cc', 'a/b/f2.cc',
[11, 12, 13], 'https://repo2')])
def testCallStackBufferLen(self):
"""Tests ``len(CallStackBuffer)`` works as expected."""
self.assertEqual(len(self.stack_buffer), 2)
def testCallStackBufferBool(self):
"""Tests ``bool`` for ``CallStackBuffer`` object works as expected."""
self.assertTrue(bool(self.stack_buffer))
self.assertFalse(bool(CallStackBuffer(0, frame_list=[])))
def testCallStackBufferIter(self):
"""Tests ``iter`` for ``CallStackBuffer`` works as expected."""
for index, frame in enumerate(self.stack_buffer.frames):
self.assertEqual(index, frame.index)
def testToCallStackForNonEmptyCallStackBuffer(self):
"""Tests ``ToCallStack`` for non empty ``CallStackBuffer`` object."""
frame_list=[StackFrame(0, 'repo/path', 'func', 'a/c.cc', 'a/c.cc',
[3, 4], 'https://repo')]
stack_buffer = CallStackBuffer(0, frame_list=frame_list)
expected_callstack = CallStack(stack_buffer.priority,
tuple(frame_list),
CallStackFormatType.DEFAULT,
LanguageType.CPP)
self.assertTupleEqual(stack_buffer.ToCallStack(), expected_callstack)
def testToCallStackForEmptyCallStackBuffer(self):
"""Tests ``ToCallStack`` for empty ``CallStackBuffer`` object."""
self.assertIsNone(CallStackBuffer(0, frame_list=[]).ToCallStack())
def testFromNoneStartOfCallStack(self):
"""Tests ``FromStartOfCallStack`` with None input."""
self.assertIsNone(CallStackBuffer.FromStartOfCallStack(None))
def testFromStartOfCallStack(self):
"""Tests ``FromStartOfCallStack`` with ``StartOfCallStack`` input."""
start_of_callstack = StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {'pid': 123})
stack_buffer = CallStackBuffer.FromStartOfCallStack(start_of_callstack)
self.assertEqual(stack_buffer.priority, start_of_callstack.priority)
self.assertEqual(stack_buffer.format_type, start_of_callstack.format_type)
self.assertEqual(stack_buffer.language_type,
start_of_callstack.language_type)
self.assertDictEqual(stack_buffer.metadata, start_of_callstack.metadata)
class StacktraceTest(AnalysisTestCase):
def testStacktraceLen(self):
"""Tests ``len`` for ``Stacktrace`` object."""
frame_list1 = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32])]
frame_list2 = [
StackFrame(0, 'src', 'func2', 'file0.cc', 'src/file0.cc', [32])]
stack1 = CallStack(0, frame_list1, None, None)
stack2 = CallStack(1, frame_list2, None, None)
stacktrace = Stacktrace((stack1, stack2), stack1)
self.assertEqual(len(stacktrace), 2)
def testStacktraceBool(self):
"""Tests ``bool`` for ``Stacktrace`` object."""
self.assertFalse(bool(Stacktrace([], None)))
frame_list1 = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32])]
frame_list2 = [
StackFrame(0, 'src', 'func2', 'file0.cc', 'src/file0.cc', [32])]
stack1 = CallStack(0, frame_list1, None, None)
stack2 = CallStack(1, frame_list2, None, None)
self.assertTrue(bool(Stacktrace((stack1, stack2), stack1)))
class StacktraceBufferTest(AnalysisTestCase):
def _DummyFilter(self, stack_buffer): # pragma: no cover
return stack_buffer
def testStacktraceBufferWithoutSignature(self):
"""Tests using least priority stack as crash_stack without signature."""
frame_list1 = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32])]
frame_list2 = [
StackFrame(0, 'src', 'func2', 'file0.cc', 'src/file0.cc', [32])]
stack1 = CallStackBuffer(0, frame_list=frame_list1)
stack2 = CallStackBuffer(1, frame_list=frame_list2)
stacktrace = StacktraceBuffer([stack1, stack2]).ToStacktrace()
self._VerifyTwoCallStacksEqual(stacktrace.crash_stack, stack1.ToCallStack())
def testSettingStackWithIsSignatureStackMetaDataAsCrashStack(self):
"""Tests using stack with signature as crash_stack with signature."""
frame_list1 = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32])]
frame_list2 = [
StackFrame(0, 'src', 'signature_func2', 'f.cc', 'src/f.cc', [32])]
stack1 = CallStackBuffer(0, frame_list=frame_list1,
metadata={'is_signature_stack': True})
stack2 = CallStackBuffer(1, frame_list=frame_list2)
stacktrace = StacktraceBuffer([stack1, stack2]).ToStacktrace()
self._VerifyTwoCallStacksEqual(stacktrace.crash_stack, stack1.ToCallStack())
def testAddFitleredStackWithNoFilters(self):
"""Tests that ``AddFilteredStack`` returns None if there is no filters."""
frame_list = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32]),
StackFrame(0, 'src', 'func2', 'file0.cc', 'src/file0.cc', [32])]
stack_buffer = CallStackBuffer(0, frame_list=frame_list)
stacktrace_buffer = StacktraceBuffer()
stacktrace_buffer.AddFilteredStack(stack_buffer)
self.assertEqual(len(stacktrace_buffer.stacks), 1)
def testFilterInfinityPriorityStackBuffer(self):
"""Tests that ``AddFilteredStack`` returns None for inf priority stack."""
stack_buffer = CallStackBuffer(priority=float('inf'))
stacktrace_buffer = StacktraceBuffer(filters=[self._DummyFilter])
stacktrace_buffer.AddFilteredStack(stack_buffer)
self.assertEqual(len(stacktrace_buffer.stacks), 0)
def testFilterEmptyStackBuffer(self):
"""Tests that ``AddFilteredStack`` returns None for empty stack buffer."""
stack_buffer = CallStackBuffer(frame_list=[])
stacktrace_buffer = StacktraceBuffer(filters=[self._DummyFilter])
stacktrace_buffer.AddFilteredStack(stack_buffer)
self.assertEqual(len(stacktrace_buffer.stacks), 0)
def testFilterAllFrames(self):
"""Tests that ``AddFilteredStack`` filters all frames and resturns None."""
frame_list = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32]),
StackFrame(0, 'src', 'func2', 'file0.cc', 'src/file0.cc', [32])]
stack_buffer = CallStackBuffer(0, frame_list=frame_list)
def _MockFilterAllFrames(stack_buffer):
stack_buffer.frames = None
return stack_buffer
stacktrace_buffer = StacktraceBuffer(filters=[_MockFilterAllFrames])
stacktrace_buffer.AddFilteredStack(stack_buffer)
self.assertEqual(len(stacktrace_buffer.stacks), 0)
def testFilterSomeFrames(self):
"""Tests that ``AddFilteredStack`` filters some frames."""
frame_list = [
StackFrame(0, 'src', 'func', 'file0.cc', 'src/file0.cc', [32]),
StackFrame(0, 'src', 'func2', 'file0.cc', 'src/file0.cc', [32])]
stack_buffer = CallStackBuffer(0, frame_list=frame_list)
def _MockKeepFirstFrame(stack):
stack.frames = stack.frames[:1]
return stack
stacktrace_buffer = StacktraceBuffer(filters=[_MockKeepFirstFrame])
stacktrace_buffer.AddFilteredStack(stack_buffer)
self._VerifyTwoCallStacksEqual(
stacktrace_buffer.stacks[0],
CallStackBuffer(stack_buffer.priority, frame_list=frame_list[:1]))
def testEmptyStacktraceBufferToStacktrace(self):
"""Tests that ``ToStacktrace`` returns None for empty stacktrace buffer."""
self.assertIsNone(StacktraceBuffer([]).ToStacktrace())