blob: f2b0268ba89ee06a4c674d4a3dda72472d96ef65 [file] [log] [blame]
# @@@ START COPYRIGHT @@@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# @@@ END COPYRIGHT @@@
#
# This script extracts data from an Optimizer trace, created
# using the following CQDs:
#
# cqd nsk_dbg_log_file 'optimizerTrace.txt';
#
# cqd NSK_DBG_GENERIC 'ON';
# cqd NSK_DBG_PRINT_COST 'ON';
# cqd NSK_DBG_PRINT_COST_LIMIT 'ON';
# cqd NSK_DBG_PRINT_LOG_PROP 'ON';
# cqd NSK_DBG_PRINT_PHYS_PROP 'ON';
# cqd NSK_DBG_PRINT_TASK 'ON';
# cqd NSK_DBG_PRINT_TASK_STACK 'ON';
#
# cqd NSK_DBG 'ON';
#
# The script extracts completed plans and shows their shape
# and costs, along with whether the plan was chosen. The
# goal of the script is to present the trace information in
# a simple and high-level way.
#
import os
import sys
import subprocess
import sets
import datetime
import argparse # requires Python 2.7
def indentLevel(line):
i = 0
while i < len(line) and line[i] == ' ':
i = i + 1
return i
class ParseTrace:
#
def __init__(self, OptimizerTraceFileName):
self.OptimizerTraceFileName = OptimizerTraceFileName
def parseTrace(self):
#
# The things we are looking for are:
#
# "Query to optimize:"
# This shows the start of a trace for a particular query
#
# "Task: 8 ParentTask: 7 Pass: 2 GroupId: 1 7 1 Create plan (0) ..."
# Plan fragments show up as a result of "Create plan" tasks being executed.
# Plan fragments may result in incomplete plans; we ignore those.
#
# "*** Qualified plan ***"
# This is the beginning of a qualified plan fragment, created by a "Create
# plan" task.
#
# "*** Chosen plan ***" and "*** Non-optimal plan ***"
# These indicate the end of a qualified plan fragment, and indicate whether
# the particular fragment was chosen for its group.
#
# "SQL node(<node description>)"
# This indicates a node within the plan. The tree structure of the nodes
# is indicated by the relative indentation of each of these. Python-like.
#
# "**** Roll-up Cost ****"
# This indicates that cost info for the subtree follows.
#
# "**** Operator Cost ****"
# This indicates that cost info for the node itself follows.
#
# "elapsed time = <floating point number>"
# This gives the cost, reduced to a single number.
#
# For now, we ignore the other information in the trace.
#
try:
f = open(self.OptimizerTraceFileName)
saveSQLNode = ''
state = 0
for line in f:
#print 'state = ' + str(state)
line = line.rstrip('\n') # get rid of trailing return character
if state == 0 and line == "Query to optimize:":
print "Trace for the following query:"
state = 1
elif state == 1:
if len(line) == 0:
state = 2
else:
print line
elif state == 2:
if line == "Query to optimize:":
state = 20 # ignore the rest of the file
tempLine = line.lstrip()
if tempLine.startswith('Task: ') and tempLine.find('Create plan'):
print
print tempLine
print
state = 3
elif state == 3:
tempLine = line.lstrip()
if tempLine == "*** Qualified plan ***":
state = 4
elif state == 4:
tempLine = line.lstrip()
if tempLine.startswith('SQL node('):
saveSQLNode = line
state = 5
elif state == 5:
tempLine = line.lstrip()
if tempLine.startswith('**** Roll-up Cost ****'):
state = 6
elif state == 6:
tempLine = line.lstrip()
if tempLine.startswith('elapsed time = '):
planCost = 'Plan cost ' + tempLine
print planCost
print
print saveSQLNode
n = indentLevel(line)
rollupCost = line[0:n] + 'Rollup cost ' + tempLine
print rollupCost
state = 9
elif state == 7:
tempLine = line.lstrip()
if tempLine.startswith('**** Roll-up Cost ****'):
state = 8
elif state == 8:
tempLine = line.lstrip()
if tempLine.startswith('elapsed time = '):
n = indentLevel(line)
rollupCost = line[0:n] + 'Rollup cost ' + tempLine
print rollupCost
state = 9
elif state == 9:
tempLine = line.lstrip()
if tempLine.startswith('**** Operator Cost ****'):
state = 10
elif state == 10:
tempLine = line.lstrip()
if tempLine.startswith('elapsed time = '):
n = indentLevel(line)
operatorCost = line[0:n] + 'Operator cost ' + tempLine
print operatorCost
state = 11
elif state == 11:
tempLine = line.lstrip()
if tempLine.startswith('SQL node('):
print line
state = 7
elif tempLine.startswith('Task: ') and tempLine.find('Create plan'):
print
print tempLine
print
state = 3
elif tempLine.startswith('*** Chosen plan ***') or tempLine.startswith('*** Non-optimal plan ***'):
print
print line
print
state = 2
f.close()
except IOError as detail:
print "Could not open " + self.OptimizerTraceFileName
print detail
# beginning of main
# process command line arguments
parser = argparse.ArgumentParser(
description='This script parses out interesting data from an optimizer debug trace.')
parser.add_argument("OptimizerTraceFileName", help='The name of the trace file you wish to parse.')
args = parser.parse_args() # exits and prints help if args are incorrect
exitCode = 0
Traceparser = ParseTrace(args.OptimizerTraceFileName)
Traceparser.parseTrace()
exit(exitCode)