aboutsummaryrefslogtreecommitdiffstats
from __future__ import division

from collections import defaultdict
from itertools import imap

import heapq

from .format import event, EVENTS_WITHOUT_TIMESTAMP, EVENTS
from .file import SchedTraceFile

def event_id(unpacked_record):
    "Given a tuple from format.unpack(), return the ID of the event"
    return unpacked_record[0]

def event_cpu(unpacked_record):
    "Given a tuple from format.unpack(), return the CPU that recorded the event"
    return unpacked_record[1]

def event_pid(unpacked_record):
    "Given a tuple from format.unpack(), return the process ID of the event"
    return unpacked_record[2]

def event_job_id(unpacked_record):
    "Given a tuple from format.unpack(), return the job ID of the event"
    return unpacked_record[3]

def event_name(unpacked_record):
    "Given a tuple from format.unpack(), return the name of the event"
    # first element of tuple from format.unpack() is event type
    return event(unpacked_record[0])

def event_time(unpacked_record):
    "Given a tuple from format.unpack(), return the time of the event"
    if unpacked_record[0] in EVENTS_WITHOUT_TIMESTAMP:
        return 0
    else:
        return unpacked_record[4]

def lookup_ids(events):
    wanted = set()
    for e in events:
        if type(e) == int:
            # directly add numeric IDs
            wanted.add(e)
        else:
            # translate strings
            wanted.add(EVENTS[e])
    return wanted


def cached(trace_attr):
    cached_val = []
    def check_cache(*args, **kargs):
        if not cached_val:
            cached_val.append(trace_attr(*args, **kargs))
        return cached_val[0]
    return check_cache

class SchedTrace(object):
    def __init__(self, trace_files):
        self.traces = [SchedTraceFile(f) for f in trace_files]

        self._names = None
        self._wcets = None
        self._periods = None
        self._phases = None
        self._partitions = None
        self._sys_rels = None

    def __len__(self):
        return sum((len(t) for t in self.traces))

    def __iter__(self):
        for t in self.traces:
            for rec in t:
                yield rec

    def events_of_type(self, *events):
        wanted = lookup_ids(events)
        for t in self.traces:
            for rec in t.events_of_type(wanted):
                yield rec

    def events_of_type_chrono(self, *events):
        wanted = lookup_ids(events)

        def by_event_time(rec):
            return (event_time(rec), rec)

        trace_iters = [imap(by_event_time, t.events_of_type(wanted))
                       for t in self.traces]

        for (when, rec) in heapq.merge(*trace_iters):
            yield rec

    def events_in_range_of_type(self, start=0, end=0, *events, **kargs):
        if 'sorted' in kargs and kargs['sorted']:
            all = self.events_of_type_chrono(*events)
        else:
            all = self.events_of_type(*events)

        for rec in all:
            if start <= event_time(rec) <= end:
                yield rec

    def active_in_interval(self, start=0, end=0):
        tasks = set()
        cores = set()
        for rec in self.events_of_type('ST_SWITCH_TO', 'ST_SWITCH_AWAY', 'ST_RELEASE'):
            if start <= event_time(rec) <= end:
                tasks.add(event_pid(rec))
                cores.add(event_cpu(rec))
        return tasks, cores

    def scheduling_intervals(self):
        for t in self.traces:
            for interval in t.scheduling_intervals():
                yield interval

    def scheduling_intervals_in_range(self, start=0, end=0):
        for t in self.traces:
            for (to, away) in t.scheduling_intervals():
                if not (end < event_time(to) or event_time(away) < start):
                    yield (to, away)

    def identify_tasks(self):
        self._names      = defaultdict(str)
        self._wcets      = defaultdict(int)
        self._periods    = defaultdict(int)
        self._phases     = defaultdict(int)
        self._partitions = defaultdict(int)
        param_id = EVENTS['ST_PARAM']
        name_id = EVENTS['ST_NAME']
        for rec in self.events_of_type(param_id, name_id):
            pid = rec[2]
            if rec[0] == param_id:
                wcet, period, phase, partition = rec[-4:]
                self._wcets[pid]   = wcet
                self._periods[pid] = period
                self._phases[pid]  = phase
                self._partitions[pid] = partition
            elif rec[0] == name_id:
                self._names[pid] = rec[-1]

    @property
    def task_wcets(self):
        if self._wcets is None:
            self.identify_tasks()
        return self._wcets

    @property
    def task_periods(self):
        if self._periods is None:
            self.identify_tasks()
        return self._periods

    @property
    def task_phases(self):
        if self._phases is None:
            self.identify_tasks()
        return self._phases

    @property
    def task_partitions(self):
        if self._partitions is None:
            self.identify_tasks()
        return self._partitions

    @property
    def task_names(self):
        if self._names is None:
            self.identify_tasks()
        return self._names

    @property
    @cached
    def system_releases(self):
        return [rec[-1] for rec in self.events_of_type('ST_SYS_RELEASE')]

    @property
    @cached
    def earliest_event_time(self):
        earliest = None
        for t in self.traces:
            for rec in t:
                if event_time(rec) > 0:
                    if earliest is None or event_time(rec) < earliest:
                        earliest = event_time(rec)
                    break
        return earliest

    @property
    @cached
    def latest_event_time(self):
        latest = None
        for t in self.traces:
            for rec in reversed(t):
                if event_time(rec) > 0:
                    if latest is None or event_time(rec) > latest:
                        latest = event_time(rec)
                    break
        return latest