-- File: PerfRoutines.mesa   last edited by
-- Sandman on September 3, 1980  4:17 PM
-- Karlton on Jun 25, 1980 2:31 AM

DIRECTORY
  BcdDefs USING [Base, CTIndex, MTIndex],
  BcdOps USING [BcdBase, CTHandle, MTHandle, NameString],
  DebugUsefulDefs USING [
    GFHandle, Name, ShortCopyREAD, ShortCopyWRITE, ShortREAD, ShortWRITE],
  DLoadState USING [
    Acquire, AcquireBcd, Invalid, MapRealToConfig, Release, ReleaseBcd],
  Event USING [AddNotifier, Item, Masks, Notifier],
  FormSW USING [DisplayItem, EnumeratedNotifyProcType, NotifyProcType],
  Gf USING [GFI, Handle, Original],
  MachineDefs USING [ConfigIndex, GFHandle, GFTIndex, NullConfig],
  MsgSW USING [Post],
  PerfCommonOps USING [cmdSW, ConditionBreaks, herald, logSW, msgSW],
  PerfOps USING [
    AddHistogram, AddLeg, addMode, ClearTables, CollectNodes, DeleteHistogram,
    DeleteLeg, Error, GetDeleteLeg, GetFromNode, GetToNode, monitorOn, ParamIndex,
    PerfMessage, PrintHistogram, PrintLegTable, PrintNodeTable, PrintTables,
    process, SetProcess, trackMode, ZeroCounts],
  PerfPrivate USING [
    HistBase, HistSpaceSize, LegTab, NodeTab, PCR, PerfControlRecord, ReadWrite,
    VersionID],
  Put USING [CR, Line, Text],
  SDDefs USING [sBreak, SD, sPerfMonitor],
  Storage USING [FreeWords, Words],
  Strings USING [AppendSubString, SubStringDescriptor],
  Time USING [Append, Current, Unpack],
  UserInput USING [ResetUserAbort, userAbort],
  Window USING [Handle];

PerfRoutines: PROGRAM
  IMPORTS
    DebugUsefulDefs, Event, FormSW, Gf, DLoadState, MsgSW, PerfCommonOps, PerfOps,
    Put, Strings, Storage, Time, UserInput
  EXPORTS PerfOps =

  PUBLIC

  BEGIN OPEN PerfOps, PerfPrivate, DebugUsefulDefs;

  perfRecord: PerfControlRecord;
  pCR: PCR ← @perfRecord;
  nodeTab: POINTER TO NodeTab;
  legTab: POINTER TO LegTab;
  histBase: HistBase;
  Table: TYPE = {hist, leg, node, pcr};
  haveTable, dirtyTable: PACKED ARRAY Table OF BOOLEAN ←
    [FALSE, FALSE, FALSE, FALSE];

  GetHistBase: PROCEDURE [mode: ReadWrite] RETURNS [HistBase] =
    BEGIN OPEN DebugUsefulDefs;
    IF ~haveTable[hist] THEN
      BEGIN
      [] ← GetPCR[read];
      histBase ← Storage.Words[HistSpaceSize];
      ShortCopyREAD[to: histBase, from: pCR.histBase, nwords: HistSpaceSize];
      haveTable[hist] ← TRUE;
      END;
    IF mode = write THEN dirtyTable[hist] ← TRUE;
    RETURN[histBase];
    END;

  GetLegTable: PROCEDURE [mode: ReadWrite] RETURNS [POINTER TO LegTab] =
    BEGIN OPEN DebugUsefulDefs;
    IF ~haveTable[leg] THEN
      BEGIN
      [] ← GetPCR[read];
      legTab ← Storage.Words[SIZE[LegTab]];
      ShortCopyREAD[to: legTab, from: pCR.legTable, nwords: SIZE[LegTab]];
      haveTable[leg] ← TRUE;
      END;
    IF mode = write THEN dirtyTable[leg] ← TRUE;
    RETURN[legTab];
    END;

  GetNodeTable: PROCEDURE [mode: ReadWrite] RETURNS [POINTER TO NodeTab] =
    BEGIN OPEN DebugUsefulDefs;
    IF ~haveTable[node] THEN
      BEGIN
      [] ← GetPCR[read];
      nodeTab ← Storage.Words[SIZE[NodeTab]];
      ShortCopyREAD[to: nodeTab, from: pCR.nodeTable, nwords: SIZE[NodeTab]];
      haveTable[node] ← TRUE;
      END;
    IF mode = write THEN dirtyTable[node] ← TRUE;
    RETURN[nodeTab];
    END;

  GetPCR: PROCEDURE [mode: ReadWrite] RETURNS [PCR] =
    BEGIN OPEN DebugUsefulDefs;
    IF ~haveTable[pcr] THEN
      BEGIN
      perf: PCR ← ShortREAD[@SDDefs.SD[SDDefs.sPerfMonitor]];
      ShortCopyREAD[to: pCR, from: perf, nwords: SIZE[PerfControlRecord]];
      haveTable[pcr] ← TRUE;
      END;
    IF mode = write THEN dirtyTable[pcr] ← TRUE;
    RETURN[pCR];
    END;

  TablesExist: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    RETURN[DebugUsefulDefs.ShortREAD[@SDDefs.SD[SDDefs.sPerfMonitor]] # NIL];
    END;

  TablesCorrectVersion: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN RETURN[GetPCR[read].version = VersionID]; END;

  PutTables: PRIVATE PROCEDURE [flush: BOOLEAN] =
    BEGIN OPEN DebugUsefulDefs;
    perf: PCR ← ShortREAD[@SDDefs.SD[SDDefs.sPerfMonitor]];
    IF flush AND haveTable[pcr] AND dirtyTable[pcr] THEN
      ShortCopyWRITE[to: perf, from: pCR, nwords: SIZE[PerfControlRecord]];
    IF haveTable[node] THEN
      BEGIN
      IF flush AND dirtyTable[node] THEN
	ShortCopyWRITE[to: pCR.nodeTable, from: nodeTab, nwords: SIZE[NodeTab]];
      Storage.FreeWords[nodeTab];
      END;
    IF haveTable[leg] THEN
      BEGIN
      IF flush AND dirtyTable[leg] THEN
	ShortCopyWRITE[to: pCR.legTable, from: legTab, nwords: SIZE[LegTab]];
      Storage.FreeWords[legTab];
      END;
    IF haveTable[hist] THEN
      BEGIN
      IF flush AND dirtyTable[hist] THEN
	ShortCopyWRITE[to: pCR.histBase, from: histBase, nwords: HistSpaceSize];
      Storage.FreeWords[histBase];
      END;
    haveTable ← dirtyTable ← [FALSE, FALSE, FALSE, FALSE];
    END;

  TurnOn: PROCEDURE =
    BEGIN OPEN DebugUsefulDefs, SDDefs;
    [] ← GetPCR[write];
    pCR.saveBreakHandler ← ShortREAD[@SD[sBreak]];
    ShortWRITE[@SD[sBreak], pCR.self];
    END;

  TurnOff: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN OPEN DebugUsefulDefs, SDDefs;
    [] ← GetPCR[write];
    IF pCR.saveBreakHandler # NIL AND pCR.self = ShortREAD[@SD[sBreak]] THEN
      BEGIN
      ShortWRITE[@SD[sBreak], pCR.saveBreakHandler];
      pCR.measuringNow ← FALSE;
      RETURN[TRUE]
      END;
    RETURN[FALSE];
    END;

  NoContext: SIGNAL = CODE;

  GetConfigAndModuleName: PROCEDURE [
    frame: MachineDefs.GFHandle, config, module: STRING] =
    BEGIN OPEN DLoadState;
    bcd: BcdOps.BcdBase;
    c: MachineDefs.ConfigIndex ← MachineDefs.NullConfig;
    cgfi: MachineDefs.GFTIndex;
    ssb: BcdOps.NameString;
    ss: Strings.SubStringDescriptor;
    [] ← Acquire[ ! Invalid => SIGNAL NoContext];
    BEGIN
    ENABLE UNWIND => Release[];
    DebugUsefulDefs.Name[module, frame]; --now get config name 
    frame ← Gf.Original[frame];
    [cgfi: cgfi, config: c] ← MapRealToConfig[Gf.GFI[frame]];
    IF c # MachineDefs.NullConfig THEN
      BEGIN OPEN BcdDefs;
      bcd ← AcquireBcd[c];
      ssb ← LOOPHOLE[bcd + bcd.ssOffset];
      config.length ← 0;
      IF bcd.nConfigs # 0 THEN
	BEGIN
	cth: BcdOps.CTHandle = @LOOPHOLE[bcd + bcd.ctOffset, Base][
	  FIRST[BcdDefs.CTIndex]];
	ss ← [base: @ssb.string, offset: cth.name, length: ssb.size[cth.name]];
	END
      ELSE
	BEGIN
	mth: BcdOps.MTHandle = @LOOPHOLE[bcd + bcd.mtOffset, Base][
	  FIRST[BcdDefs.MTIndex]];
	ss ← [base: @ssb.string, offset: mth.name, length: ssb.size[mth.name]];
	END;
      Strings.AppendSubString[config, @ss];
      ReleaseBcd[bcd];
      END
    ELSE SIGNAL NoContext;
    END; -- ELBANE
    Release[];
    RETURN;
    END;

  handle: Window.Handle ← PerfCommonOps.logSW;

  PostError: PROCEDURE [error: Error] =
    BEGIN
    MsgSW.Post[
      PerfCommonOps.msgSW,
      SELECT error FROM
	notOn => "!Please Start PerfMonitor and then try again"L,
	goofUp =>
	  "!Goofed up PerfMonitor by use of Worry on/off during measurements"L,
	syntaxError => "!Syntax Error in Input"L,
	badLeg => "!Bad Leg Index Specified"L,
	badChar => "!Illegal Character"L,
	noLegRoom => "!No More Room in Leg Table"L,
	badNode => "!Bad Node Index Specified"L,
	version => "!PerfTool has Incorrect Version"L,
	noContext => "!No Context"L,
	badProcess => "!Invalid Process specified"L,
	ENDCASE => "?"L];
    END;

  PutMessage: PROCEDURE [message: PerfMessage] =
    BEGIN
    Put.Text[
      handle,
      SELECT message FROM
	totalTime => "Total Elapsed Time of Measurements =     "L,
	elapsedTime => "Elapsed Time less PerfMonitor Overhead = "L,
	totalOverhead => "Total Overhead of PerfMonitor Breaks =   "L,
	nBreaks => "Total number of Perf Breaks handled =    "L,
	avgOverhead => "Average Overhead per Perf Break =        "L,
	percentInMont => "% of Total Time spent in PerfMonitor =   "L,
	tooSmall => "Too many breakpoints!"L,
	aborted => "... aborted"L,
	ENDCASE => "?"L];
    IF message = aborted OR message = tooSmall THEN Put.CR[handle];
    END;

  WriteNodeTableHeader: PROCEDURE =
    BEGIN
    Put.Line[
      handle,
      " - - - - - - N O D E   T A B L E   C O N T E N T S - - - - - - - - - -"L];
    IF UserInput.userAbort THEN RETURN;
    Put.Line[handle, "Node Global  Program  Number of  Config   Module"L];
    IF UserInput.userAbort THEN RETURN;
    Put.Line[handle, " Id  Frame   Counter  References Name     Name"L];
    IF UserInput.userAbort THEN RETURN;
    Put.Line[handle, "---- ------  -------  ---------- -------- --------"L];
    END;

  WriteLegTableHeader: PROCEDURE =
    BEGIN
    Put.Line[
      handle,
      "- - - - - - - L E G    T A B L E    C O N T E N T S - - - - - - - - -"L];
    IF UserInput.userAbort THEN RETURN;
    Put.Line[
      handle,
      "Leg  From  To     # of Times   Total Time      Average Time   % of"L];
    IF UserInput.userAbort THEN RETURN;
    Put.Line[
      handle,
      "Id   Node  Node   Referenced   sec.msec:usec   sec.msec:usec  Time"L];
    IF UserInput.userAbort THEN RETURN;
    Put.Line[
      handle,
      "---  ----  ----   ----------   -------------   -------------  -----"L];
    END;

  PutHerald: PROCEDURE =
    BEGIN OPEN Time;
    s: STRING ← [22];
    Put.Line[handle, PerfCommonOps.herald];
    Append[s, Unpack[Current[]]];
    s.length ← s.length - 3;
    Put.Line[handle, s];
    Put.CR[handle];
    RETURN
    END;

  SetDefaults: PROCEDURE [index: ParamIndex] =
    BEGIN
    pcr: PCR ← GetPCR[write];
    IF index # mon THEN
      BEGIN
      monitorOn ← FALSE;
      FormSW.DisplayItem[PerfCommonOps.cmdSW, LOOPHOLE[ParamIndex[mon]]];
      END;
    IF index # track THEN
      BEGIN
      trackMode ← pcr.trackLeg ← all;
      FormSW.DisplayItem[PerfCommonOps.cmdSW, LOOPHOLE[ParamIndex[track]]];
      END;
    IF index # add THEN
      BEGIN
      addMode ← pcr.addLeg ← none;
      FormSW.DisplayItem[PerfCommonOps.cmdSW, LOOPHOLE[ParamIndex[add]]];
      END;
    IF index # setProcess THEN
      BEGIN
      IF process # NIL THEN process.length ← 0;
      FormSW.DisplayItem[PerfCommonOps.cmdSW, LOOPHOLE[ParamIndex[process]]];
      END;
    pcr.newSession ← FALSE;
    PutHerald[];
    RETURN
    END;

  ParamNotify: FormSW.NotifyProcType =
    BEGIN
    MsgSW.Post[PerfCommonOps.msgSW, ""L];
    IF ~TablesExist[] THEN GOTO noTables;
    IF ~TablesCorrectVersion[] THEN GOTO badVersion;
    [] ← GetPCR[read];
    IF pCR.newSession THEN SetDefaults[LOOPHOLE[index, ParamIndex]];
    SELECT LOOPHOLE[index, ParamIndex] FROM
      mon => IF monitorOn THEN TurnOn[] ELSE IF ~TurnOff[] THEN PostError[goofUp];
      collect => CollectNodes[];
      init => ClearTables[];
      zero => ZeroCounts[];
      condition => PerfCommonOps.ConditionBreaks[];
      printtables => PrintTables[];
      printnodes => PrintNodeTable[];
      printlegs => PrintLegTable[];
      addleg => AddLeg[from: GetFromNode[], to: GetToNode[]];
      delete => DeleteLeg[index: GetDeleteLeg[]];
      setProcess => SetProcess[];
      add => GetPCR[write].addLeg ← addMode;
      track => GetPCR[write].trackLeg ← trackMode;
      addhist => AddHistogram[];
      delhist => DeleteHistogram[];
      printhist => PrintHistogram[];
      ENDCASE;
    UserInput.ResetUserAbort[];
    EXITS noTables => PostError[notOn]; badVersion => PostError[version];
    END;

  ParamEnumNotify: FormSW.EnumeratedNotifyProcType = {
    ParamNotify[sw, item, index]};

  Cleanup: Event.Notifier = {PutTables[why = resumeDebuggee]};

  -- Mainline code

  cleanupItem: Event.Item ←
    [link:, eventMask: Event.Masks[resumeDebuggee] + Event.Masks[abortSession],
      eventProc: Cleanup];

  Event.AddNotifier[@cleanupItem];

  END.