Skip to content

Instantly share code, notes, and snippets.

@wvdschel
Created May 7, 2012 12:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wvdschel/2627572 to your computer and use it in GitHub Desktop.
Save wvdschel/2627572 to your computer and use it in GitHub Desktop.
Cats parser
# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment