#!/usr/bin/env python2.7
# Copyright (C) Microsoft Corporation. All rights reserved.
# This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details.

"""Analyses the ETW dump file fors dxcompiler output as generated by hcttrace."""

import argparse
import datetime
import xml.etree.ElementTree as ET

# Task values.
DXCompilerInitialization = 1;
DXCompilerShutdown = 2;
DXCompilerCreateInstance = 3;
DXCompilerIntelliSenseParse = 4;
DXCompilerCompile = 5;
DXCompilerPreprocess = 6;
DXCompilerDisassemble = 7;

# Opcode values
OpcodeStart = 1;
OpcodeStop = 2;

# Namespace dictionary.
ns = {'e': 'http://schemas.microsoft.com/win/2004/08/events/event'}

def write_basic_info(node):
    '''Writes computer information'''
    print("Basic information:")
    print("CPU Speed: %s" % node.find("e:Data[@Name='CPUSpeed']", ns). text)
    print("Processor count: %s" % node.find("e:Data[@Name='NumberOfProcessors']", ns). text)
    print("Events lost: %s" % node.find("e:Data[@Name='EventsLost']", ns). text)
    print("Pointer size: %s" % node.find("e:Data[@Name='PointerSize']", ns). text)


def write_compile_times(root):
    '''Prints out compilation times.'''
    compilations = {}
    for e in root:
        system_node = e.find("e:System", ns)
        if system_node is None:
            continue
        channel = system_node.find("e:Channel", ns)
        if channel is None or channel.text <> "Microsoft-Windows-DXCompiler-API/Analytic":
            continue
        task = int(system_node.find("e:Task", ns).text)
        opcode = int(system_node.find("e:Opcode", ns).text)
        pid = int(system_node.find("e:Execution", ns).attrib['ProcessID'])
        tid = int(system_node.find("e:Execution", ns).attrib['ThreadID'])
        time_created = system_node.find("e:TimeCreated", ns).attrib['SystemTime']
        # Something like:
        # 2016-02-02T00:10:51.619434100Z
        # Unfortunately datetime doesn't have enough precision, so we make do with this:
        # 2016-02-02T00:10:39.630081
        if task == DXCompilerCompile:
            time_created = time_created[:26]
            cid = '{0},{1}'.format(pid, tid)
            parsed_time_created = datetime.datetime.strptime(time_created, "%Y-%m-%dT%H:%M:%S.%f")
            if opcode == OpcodeStart:
                #print "Start at %s" % time_created
                compilations[cid] = parsed_time_created
            else:
                old_parsed_time_created = compilations[cid]
                print "Compilation took %s" % str(parsed_time_created - old_parsed_time_created)


def main():
  parser = argparse.ArgumentParser(description=__doc__)
  parser.add_argument('-v', '--verbose', action='store_true',
                      help='Show verbose output')
  parser.add_argument('dumpfiles', nargs='+')
  args = parser.parse_args()

  for dumpfile in args.dumpfiles:
    if args.verbose:
      print >>sys.stderr, 'Scanning for dxcompiler events in dumpfile: %s' % (dumpfile,)
    tree = ET.parse(dumpfile)
    root = tree.getroot()
    write_basic_info(root.find('e:Event/e:EventData', ns))
    write_compile_times(root)
    # Other interesting things:
    # errors, working set, additional stats

if __name__ == '__main__':
  main()
