import lldb
from intelpt_testcase import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *


class TestTraceEvents(TraceIntelPTTestCaseBase):
    @testSBAPIAndCommands
    def testCPUEvents(self):
        trace_description_file_path = os.path.join(
            self.getSourceDir(),
            "intelpt-multi-core-trace",
            "trace_missing_threads.json",
        )
        self.traceLoad(
            traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"]
        )

        self.expect(
            "thread trace dump instructions 3 -e --forward -c 5",
            substrs=[
                """thread #3: tid = 3497496
    0: (event) HW clock tick [40450075477621505]
    1: (event) CPU core changed [new CPU=51]
    2: (event) HW clock tick [40450075477657246]
    3: (event) trace synchronization point [offset = 0x0x1331]
  m.out`foo() + 65 at multi_thread.cpp:12:21"""
            ],
        )

        self.expect(
            "thread trace dump instructions 3 -e --forward -c 5 -J",
            substrs=[
                """{
    "id": 0,
    "event": "HW clock tick",
    "hwClock": 40450075477621505
  },
  {
    "id": 1,
    "event": "CPU core changed",
    "cpuId": 51
  }"""
            ],
        )

    @skipIfNoIntelPT
    @testSBAPIAndCommands
    def testPauseEvents(self):
        """
        Everytime the target stops running on the CPU, a 'disabled' event will
        be emitted, which is represented by the TraceCursor API as a 'paused'
        event.
        """
        self.expect(
            "target create "
            + os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "a.out")
        )
        self.expect("b 12")
        self.expect("r")
        self.traceStartThread()
        self.expect("n")
        self.expect("n")
        self.expect("si")
        self.expect("si")
        self.expect("si")
        # We ensure that the paused events are printed correctly forward
        self.expect(
            "thread trace dump instructions -e -f",
            patterns=[
                rf"""thread #1: tid = .*
    0: \(event\) trace synchronization point \[offset \= 0x0xec0\]
    1: \(event\) hardware disabled tracing
  a.out`main \+ 23 at main.cpp:12
    2: {ADDRESS_REGEX}    movl .*
    3: \(event\) software disabled tracing
    4: {ADDRESS_REGEX}    addl .*
    5: {ADDRESS_REGEX}    movl .*
    6: \(event\) software disabled tracing
  a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4
    7: {ADDRESS_REGEX}    movl .*
  a.out`main \+ 41 \[inlined\] inline_function\(\) \+ 7 at main.cpp:5
    8: {ADDRESS_REGEX}    movl .*
    9: {ADDRESS_REGEX}    addl .*
    10: {ADDRESS_REGEX}    movl .*
  a.out`main \+ 52 \[inlined\] inline_function\(\) \+ 18 at main.cpp:6
    11: {ADDRESS_REGEX}    movl .*
  a.out`main \+ 55 at main.cpp:14
    12: {ADDRESS_REGEX}    movl .*
    13: {ADDRESS_REGEX}    addl .*
    14: {ADDRESS_REGEX}    movl .*
    15: \(event\) software disabled tracing
  a.out`main \+ 63 at main.cpp:16
    16: {ADDRESS_REGEX}    callq  .* ; symbol stub for: foo\(\)
    17: \(event\) software disabled tracing
  a.out`symbol stub for: foo\(\)
    18: {ADDRESS_REGEX}    jmpq"""
            ],
        )

        # We ensure that the paused events are printed correctly backward
        self.expect(
            "thread trace dump instructions -e --id 18",
            patterns=[
                rf"""thread #1: tid = .*
  a.out`symbol stub for: foo\(\)
    18: {ADDRESS_REGEX}    jmpq .*
    17: \(event\) software disabled tracing
  a.out`main \+ 63 at main.cpp:16
    16: {ADDRESS_REGEX}    callq  .* ; symbol stub for: foo\(\)
    15: \(event\) software disabled tracing
  a.out`main \+ 60 at main.cpp:14
    14: {ADDRESS_REGEX}    movl .*
    13: {ADDRESS_REGEX}    addl .*
    12: {ADDRESS_REGEX}    movl .*
  a.out`main \+ 52 \[inlined\] inline_function\(\) \+ 18 at main.cpp:6
    11: {ADDRESS_REGEX}    movl .*
  a.out`main \+ 49 \[inlined\] inline_function\(\) \+ 15 at main.cpp:5
    10: {ADDRESS_REGEX}    movl .*
    9: {ADDRESS_REGEX}    addl .*
    8: {ADDRESS_REGEX}    movl .*
  a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4
    7: {ADDRESS_REGEX}    movl .*
    6: \(event\) software disabled tracing
  a.out`main \+ 31 at main.cpp:12
    5: {ADDRESS_REGEX}    movl .*
    4: {ADDRESS_REGEX}    addl .*
    3: \(event\) software disabled tracing
    2: {ADDRESS_REGEX}    movl .*
    1: \(event\) hardware disabled tracing
    0: \(event\) trace synchronization point \[offset \= 0x0xec0\]"""
            ],
        )
