diff --git a/libs/utils/trace.py b/libs/utils/trace.py index f1cff7e3d07faf75f9b9ab26298fc7469dd1f135..39f6269374e15f6394aefd7fec283d2069c187fe 100644 --- a/libs/utils/trace.py +++ b/libs/utils/trace.py @@ -51,6 +51,10 @@ class Trace(object): # Maximum timespan for all collected events self.time_range = 0 + # Time the system was overutilzied + self.overutilized_time = 0 + self.overutilized_prc = 0 + # The dictionary of tasks descriptors available in the dataset self.tasks = {} @@ -147,6 +151,15 @@ class Trace(object): logging.info('Collected events spans a %.3f [s] time interval', self.time_range) + # Build a stat on trace overutilization + if self.hasEvents('sched_overutilized'): + df = self.df('sched_overutilized') + self.overutilized_time = df[df.overutilized == 1].len.sum() + self.overutilized_prc = 100. * self.overutilized_time / self.time_range + + logging.info('Overutilized time: %.6f [s] (%.3f%% of trace time)', + self.overutilized_time, self.overutilized_prc) + def getTasks(self, dataframe=None, task_names=None, name_key='comm', pid_key='pid'): # """ Helper function to get PIDs of specified tasks @@ -305,3 +318,11 @@ class Trace(object): [ccol > 2e9, ccol > 0, ccol > -2e9], ['Optimal Accept', 'SchedTune Accept', 'SchedTune Reject'], 'Suboptimal Reject') + def _sanitize_SchedOverutilized(self): + if not self.hasEvents('sched_overutilized'): + return + # Add a column with overutilized status duration + df = self.df('sched_overutilized') + df['start'] = df.index + df['len'] = (df.start - df.start.shift()).fillna(0).shift(-1) + df.drop('start', axis=1, inplace=True) diff --git a/libs/utils/trace_analysis.py b/libs/utils/trace_analysis.py index 35dfa4a4cca0840c1662b1883f3ac4d171668fd7..866705d503f8046b63c9ef1843e170d7f09f01e2 100644 --- a/libs/utils/trace_analysis.py +++ b/libs/utils/trace_analysis.py @@ -128,6 +128,7 @@ class TraceAnalysis(object): axes.grid(True); axes.set_xticklabels([]) axes.set_xlabel('') + self.plotOverutilized(axes) axes = pltaxes[1] axes.set_title('LITTLE Cluster'); @@ -143,6 +144,7 @@ class TraceAnalysis(object): axes.set_xlim(self.x_min, self.x_max); axes.set_ylabel('MHz') axes.grid(True); + self.plotOverutilized(axes) # Save generated plots into datadir figname = '{}/cluster_freqs.png'.format(self.plotsdir) @@ -253,6 +255,32 @@ class TraceAnalysis(object): lcpus = set(cpus) & set(self.platform['clusters']['little']) self.__plotCPU(lcpus, "LITTLE") + def plotOverutilized(self, axes=None): + if not self.trace.hasEvents('sched_overutilized'): + logging.warn('Events [sched_overutilized] not found, '\ + 'plot DISABLED!') + return + + # Build sequence of overutilization "bands" + df = self.trace.df('sched_overutilized') + bands = [(t, df['len'][t], df['overutilized'][t]) for t in df.index] + + # If not axis provided: generate a standalone plot + if not axes: + gs = gridspec.GridSpec(1, 1) + plt.figure(figsize=(16, 1)) + axes = plt.subplot(gs[0,0]) + axes.set_title('System Status {white: EAS mode, red: Non EAS mode}'); + axes.set_xlim(self.x_min, self.x_max); + axes.grid(True); + + # Otherwise: draw overutilized bands on top of the specified plot + for (t1,td,overutilized) in bands: + if not overutilized: + continue + t2 = t1+td + axes.axvspan(t1, t2, facecolor='r', alpha=0.1) + def plotTasks(self, tasks=None): if not self.trace.hasEvents('sched_load_avg_task'): logging.warn('Events [sched_load_avg_task] not found, '\ @@ -307,6 +335,7 @@ class TraceAnalysis(object): axes.grid(True); axes.set_xticklabels([]) axes.set_xlabel('') + self.plotOverutilized(axes) # Plot CPUs residency axes = plt.subplot(gs[1,0]); @@ -323,6 +352,7 @@ class TraceAnalysis(object): axes.grid(True); axes.set_xticklabels([]) axes.set_xlabel('') + self.plotOverutilized(axes) # Plot PELT signals axes = plt.subplot(gs[2,0]); @@ -331,6 +361,7 @@ class TraceAnalysis(object): data.plot(ax=axes, drawstyle='steps-post'); axes.set_xlim(self.x_min, self.x_max); axes.grid(True); + self.plotOverutilized(axes) # Save generated plots into datadir figname = '{}/task_util_{}.png'.format(self.plotsdir, task_name) @@ -401,6 +432,7 @@ class TraceAnalysis(object): axes.grid(True); axes.set_xticklabels([]) axes.set_xlabel('') + self.plotOverutilized(axes) # Plot2: energy and capacity variations axes = plt.subplot(gs[1,:]); @@ -416,6 +448,7 @@ class TraceAnalysis(object): axes.grid(True); axes.set_xticklabels([]) axes.set_xlabel('') + self.plotOverutilized(axes) # Plot3: energy payoff axes = plt.subplot(gs[2,:]); @@ -430,12 +463,15 @@ class TraceAnalysis(object): axes.grid(True); axes.set_xticklabels([]) axes.set_xlabel('') + self.plotOverutilized(axes) # Plot4: energy deltas (kernel and host computed values) axes = plt.subplot(gs[3,:]); axes.set_title('Energy Deltas Values'); df[['nrg_delta', 'nrg_diff_pct']].plot(ax=axes, style=['ro', 'b+']); axes.set_xlim(self.x_min, self.x_max); + axes.grid(True); + self.plotOverutilized(axes) # Save generated plots into datadir figname = '{}/ediff_time.png'.format(self.plotsdir)