- # Stats about a single log
- class Log
- # Load a file and parse every line as a print.
- def self.parse(file_path)
- Log.new(file_path, File.readlines(file_path))
- end
- # Number of lines in the file and number of lines that are valid CATS prints
- attr_reader :linecount, :cats_linecount,
- # Number of characters that are in the file, part of CATS prints and part of CATS header overhead, respectively
- :charcount, :cats_charcount, :cats_header_charcount,
- # Hash of trace symbols and trace lines, by symbol name/line identifier, and list of TracePrints
- :symbols, :lines, :prints,
- # Name of the log
- :name
- def initialize(log_name, log_lines = [])
- @name = log_name
- @linecount = @cats_linecount = @charcount = @cats_charcount = @cats_header_charcount = 0
- @symbols = {}
- @lines = {}
- @prints = []
- log_lines.each { |line| self << line }
- end
- # Add prints to this log, accepts either strings, TraceLines or TracePrints, or an array of these things
- def <<(trace_line_or_print)
- trace_print = nil
- if trace_line_or_print.is_a? TraceLine # TraceLine
- # Add all the prints
- self << trace_line_or_print.trace_prints
- return nil
- elsif trace_line_or_print.is_a? Array # Array
- trace_line_or_print.each do |print|
- self << print
- end
- rerturn nil
- elsif trace_line_or_print.is_a? TracePrint # TracePrint
- trace_print = trace_line_or_print
- else # String or others
- # It's not a TracePrint, trying making one
- if TracePrint.cats? trace_line_or_print
- trace_print = TracePrint.new(trace_line_or_print)
- else # Not a CATS print or a corrupted print
- @linecount += 1
- @charcount += trace_line_or_print.to_s.length
- return nil
- end
- end
- # Beyond this points, trace_print is certainly a TracePrint
- @linecount += 1
- @charcount += trace_print.to_s.length
- @cats_linecount += 1
- @cats_charcount += trace_print.to_s.length
- @cats_header_charcount += trace_print.to_s.length - trace_print.payload.length
- @lines[trace_print.trace_line] ||= TraceLine.new(trace_print.trace_symbol, trace_print.line)
- @symbols[trace_print.trace_symbol] ||= TraceSymbol.new(trace_print.trace_symbol)
- @symbols[trace_print.trace_symbol] << trace_print
- @lines[trace_print.trace_line] << trace_print
- @prints << trace_print
- nil
- end
- # Combine two logs
- def +(log)
- raise "TODO"
- end
- end
- # Stats about specific trace symbol
- class TraceSymbol
- attr_reader :name, :lines, :prints, :charcount
- def initialize(trace_symbol)
- @name = trace_symbol
- @charcount = 0
- @lines = {}
- @prints = []
- end
- def <<(trace_print)
- @lines[trace_print.trace_line] ||= TraceLine.new(trace_print.trace_symbol, trace_print.line)
- @lines[trace_print.trace_line] << trace_print
- @prints << trace_print
- @charcount += trace_print.to_s.length
- end
- def +(trace_symbol)
- raise "TODO"
- end
- end
- # Stats about specific TraceXxx calls
- class TraceLine
- attr_reader :symbol, :line, :trace_line, :prints, :charcount
- alias :name :trace_line
- def initialize(symbol, line)
- @symbol = symbol
- @line = line
- @charcount = 0
- @trace_line = "%s:%04d" % [@symbol, @line]
- @prints = []
- end
- def <<(trace_print)
- @prints << trace_print
- @charcount += trace_print.to_s.length
- end
- def +(trace_line)
- raise "TODO"
- end
- end
- class TracePrint
- # A5 00090.005.621 euApp: 382 /ceapsubtitle_m(01077) SUBTITLE: type = Digital
- LEGACY_CATS = %r{
- (?<cpu> [AP]){0}
- (?<level> \d){0}
- (?<timestamp>\d{5}\.\d{3}.\d{3}){0}
- (?<process> [^:]+){0}
- (?<thread_id>[^ ]+){0}
- (?<symbol> [^\(]+){0}
- (?<line> \d+){0}
- (?<payload> .*){0}
- \g<cpu>\g<level>\s\g<timestamp>\s+\g<process>:\s*\g<thread_id>\s+\g<symbol>\(\g<line>\)\s*\g<payload>
- }x
- # 5P23.46451 plfapp:455 /legacy/printf:0000 ##papi_mute_smt_MuteOutputs ## outputs=0x00 OutputsMuted = 0x00
- MODERN_CATS = %r{
- (?<cpu> [AP]){0}
- (?<level> \d){0}
- (?<timestamp>\d+\.\d{5}){0}
- (?<process> [^:]+){0}
- (?<thread_id>\d+){0}
- (?<symbol> [^:]+){0}
- (?<line> \d+){0}
- (?<payload> .*){0}
- \g<level>\g<cpu>\g<timestamp>\t+\g<process>:\g<thread_id>\t+\g<symbol>:\g<line>\t*\g<payload>
- }x
- def self.cats?(cats_print)
- cats_print && not(cats_print[LEGACY_CATS].nil? && cats_print[MODERN_CATS].nil?)
- end
- def initialize(cats_print)
- raise Error.new("#{cats_prints.inspect} is not a CATS prints.") unless TracePrint.cats?(cats_print)
- match = LEGACY_CATS.match(cats_print) || MODERN_CATS.match(cats_print)
- # Filter out second dot from timestamp for legacy prints
- @time = match[:timestamp].sub(/\.(\d\d\d)$/, '\1')
- @line = match[:line].to_i
- @trace_symbol = match[:symbol]
- @trace_line = "%s:%04d" % [@trace_symbol, @line]
- @payload = match[:payload]
- @cpu = match[:cpu]
- @level = match[:level]
- @process = match[:process]
- @thread_id = match[:thread_id]
- @payload = match[:payload]
- @raw = match.to_s
- end
- attr_reader :time, :trace_line, :line, :trace_symbol, :payload, :thread_id, :process, :cpu, :level, :raw
- def to_s
- @raw.clone
- end
- end