<commit_before># SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2015, ARM Limited and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from enum import Enum

import numpy as np
import pandas as pd

from lisa.analysis.base import TraceAnalysisBase
from lisa.utils import memoized
from lisa.trace import requires_events

class StateInt(int):
    """
    An tweaked int for :class:`lisa.analysis.tasks.TaskState`
    """
    def __new__(cls, value, char="", doc=""):
        new = super().__new__(cls, value)
        new.char = char
        new.__doc__ = doc
        return new

    def __or__(self, other):
        char = self.char

        if other.char:
            char = "|".join(char + other.char)

        return type(self)(
            int(self) | int(other),
            char=(self.char + other.char))

class TaskState(StateInt, Enum):
    """
    Represents the task state as visible in sched_switch

    * Values are extracted from include/linux/sched.h
    * Chars are extracted from fs/proc/array.c:get_task_state()
    """
    #pylint-suppress: bad-whitespace
    TASK_RUNNING         = 0x0000, "R", "Running"
    TASK_INTERRUPTIBLE   = 0x0001, "S", "Sleeping"
    TASK_UNINTERRUPTIBLE = 0x0002, "D", "Disk sleep"
    # __ has a special meaning in Python so let's not do that
    TASK_STOPPED         = 0x0004, "T", "Stopped"
    TASK_TRACED          = 0x0008, "t", "Tracing stop"

    EXIT_DEAD            = 0x0010, "X", "Dead"
    EXIT_ZOMBIE          = 0x0020, "Z", "Zombie"

    # Apparently not visible in traces
    # EXIT_TRACE           = (EXIT_ZOMBIE[0] | EXIT_DEAD[0])

    TASK_PARKED          = 0x0040, "P", "Parked"
    TASK_DEAD            = 0x0080, "I", "Idle"
    TASK_WAKEKILL        = 0x0100
    TASK_WAKING          = 0x0200, "W", "Waking" # LISA-only char definition
    TASK_NOLOAD          = 0x0400
    TASK_NEW             = 0x0800
    TASK_STATE_MAX       = 0x1000

    # LISA-only, used to differenciate runnable (R) vs running (A)
    TASK_ACTIVE          = 0x2000, "A", "Active"

    @classmethod
    def list_reported_states(cls):
        """
        List the states that can be reported in a ``sched_switch`` trace

        See include/linux/sched.h:TASK_REPORT
        """
        return [state for state in list(cls) if state <= cls.TASK_DEAD]

    # Could use IntFlag instead once we move to Python 3.6
    @classmethod
    def sched_switch_str(cls, value):
        """
        Get the task state string that would be used in a ``sched_switch`` event

        :param value: The task state value
        :type value: int

        Tries to emulate what is done in include/trace/events:TRACE_EVENT(sched_switch)
        """
        if any([value & state.value for state in cls.list_reported_states()]):
            res = "|".join([state.char for state in cls.list_reported_states()
                            if state.value & value])
        else:
            res = cls.TASK_RUNNING.char

        # Flag the presence of unreportable states with a "+"
        if any([value & state.value for state in list(cls)
                if state not in cls.list_reported_states()]):
            res += "+"

        return res

class TasksAnalysis(TraceAnalysisBase):
    """
    Support for Tasks signals analysis.

    :param trace: input Trace object
    :type trace: :class:`trace.Trace`
    """

    name = 'tasks'

    def __init__(self, trace):
        super(TasksAnalysis, self).__init__(trace)

###############################################################################
# DataFrame Getter Methods
###############################################################################

    @requires_events('sched_wakeup')
    def df_tasks_wakeups(self):
        """
        The number of wakeups per task

        :returns: a :class:`pandas.DataFrame` with:

          * Task PIDs as index
          * A ``wakeups`` column (The number of wakeups)
        """
        df = self.trace.df_events('sched_wakeup')

        wakeups = df.groupby('pid').count()["comm"]
        df = pd.DataFrame(wakeups).rename(columns={"comm" : "wakeups"})
        df["comm"] = df.index.map(self.trace.get_task_by_pid)

        return df

    @df_tasks_wakeups.used_events
    def df_top_wakeup(self, min_wakeups=100):
        """
        Tasks which wakeup more frequently than a specified threshold.

        :param min_wakeups: minimum number of wakeups
        :type min_wakeups: int<commit_msg>lisa.analysis.task: Add TaskAnalysis.cpus_of_tasks()<commit_after># SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2015, ARM Limited and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from enum import Enum

import numpy as np
import pandas as pd

from lisa.analysis.base import TraceAnalysisBase
from lisa.utils import memoized
from lisa.trace import requires_events

class StateInt(int):
    """
    An tweaked int for :class:`lisa.analysis.tasks.TaskState`
    """
    def __new__(cls, value, char="", doc=""):
        new = super().__new__(cls, value)
        new.char = char
        new.__doc__ = doc
        return new

    def __or__(self, other):
        char = self.char

        if other.char:
            char = "|".join(char + other.char)

        return type(self)(
            int(self) | int(other),
            char=(self.char + other.char))

class TaskState(StateInt, Enum):
    """
    Represents the task state as visible in sched_switch

    * Values are extracted from include/linux/sched.h
    * Chars are extracted from fs/proc/array.c:get_task_state()
    """
    #pylint-suppress: bad-whitespace
    TASK_RUNNING         = 0x0000, "R", "Running"
    TASK_INTERRUPTIBLE   = 0x0001, "S", "Sleeping"
    TASK_UNINTERRUPTIBLE = 0x0002, "D", "Disk sleep"
    # __ has a special meaning in Python so let's not do that
    TASK_STOPPED         = 0x0004, "T", "Stopped"
    TASK_TRACED          = 0x0008, "t", "Tracing stop"

    EXIT_DEAD            = 0x0010, "X", "Dead"
    EXIT_ZOMBIE          = 0x0020, "Z", "Zombie"

    # Apparently not visible in traces
    # EXIT_TRACE           = (EXIT_ZOMBIE[0] | EXIT_DEAD[0])

    TASK_PARKED          = 0x0040, "P", "Parked"
    TASK_DEAD            = 0x0080, "I", "Idle"
    TASK_WAKEKILL        = 0x0100
    TASK_WAKING          = 0x0200, "W", "Waking" # LISA-only char definition
    TASK_NOLOAD          = 0x0400
    TASK_NEW             = 0x0800
    TASK_STATE_MAX       = 0x1000

    # LISA-only, used to differenciate runnable (R) vs running (A)
    TASK_ACTIVE          = 0x2000, "A", "Active"

    @classmethod
    def list_reported_states(cls):
        """
        List the states that can be reported in a ``sched_switch`` trace

        See include/linux/sched.h:TASK_REPORT
        """
        return [state for state in list(cls) if state <= cls.TASK_DEAD]

    # Could use IntFlag instead once we move to Python 3.6
    @classmethod
    def sched_switch_str(cls, value):
        """
        Get the task state string that would be used in a ``sched_switch`` event

        :param value: The task state value
        :type value: int

        Tries to emulate what is done in include/trace/events:TRACE_EVENT(sched_switch)
        """
        if any([value & state.value for state in cls.list_reported_states()]):
            res = "|".join([state.char for state in cls.list_reported_states()
                            if state.value & value])
        else:
            res = cls.TASK_RUNNING.char

        # Flag the presence of unreportable states with a "+"
        if any([value & state.value for state in list(cls)
                if state not in cls.list_reported_states()]):
            res += "+"

        return res

class TasksAnalysis(TraceAnalysisBase):
    """
    Support for Tasks signals analysis.

    :param trace: input Trace object
    :type trace: :class:`trace.Trace`
    """

    name = 'tasks'

    def __init__(self, trace):
        super(TasksAnalysis, self).__init__(trace)

    @requires_events('sched_switch')
    def cpus_of_tasks(self, tasks):
        """
        Return the list of CPUs where the ``tasks`` executed.

        :param tasks: Task names or PIDs to look for.
        :type tasks: list(int or str)
        """
        trace = self.trace
        pids = [trace.get_task_pid(task) for task in tasks]
        df = trace.df_events('sched_switch')[['next_pid', '__cpu']]
        df = df[df['next_pid'].isin(pids)]
        cpus = df['__cpu'].unique()

        return sorted(cpus)

###############################################################################
# DataFrame Getter Methods
###############################################################################

    @requires_events('sched_wakeup')
    def df_tasks_wakeups(self):
        """
        The number of wakeups per task

        :returns: a :class:`pandas.DataFrame` with:

          * Task PIDs as index
          * A ``wakeups`` column (The number of wakeups)
        """
        df = self.trace.df_events('sched_wakeup')

        wakeups = df.groupby('pid').count()["comm"]
        df = pd.DataFrame(wakeups).rename(columns={"comm" : "wakeups"})
        df["comm"] = df.index.map(self.trace.get_task_by_pid)

        return df

    @df_tasks_wakeups.used_events
    def df_top_wakeup(self, min_wakeups=100):
        """
        Tasks which wakeup more frequently than a specified threshold.

        :param min_wakeups: minimum number of wakeups
        :type min_wakeups: int
{'fraction_non_alphanumeric': 0.12983770287141075, 'fraction_numerical': 0.01767021991741093, 'mean_word_length': 2.5506307534947155, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7449695', 'n_tokens_mistral': 3332, 'n_tokens_neox': 3142, 'n_words': 945}