internal void RaiseStartScope(RoutineInfo r) { if (OnStartScope != null) OnStartScope(r); }
internal void RaiseEndScope(RoutineInfo r) { if (OnEndScope != null) OnEndScope(r); }
/// <summary> /// Generates a call statement for a given routine. /// The routine has already been instrumented. /// </summary> /// <param name="t"></param> /// <param name="ArgsValues"></param> private void MakeScopeCreateProc(RoutineInfo ri, string[] ArgsValues, string[] InOutArgsValues) { CommonTree t = ri.ParsedTree; StringBuilder sb = new StringBuilder(); string spName = ri.Name; // build call StringBuilder sbCall = new StringBuilder(string.Format("call `{0}`( ", spName)); // adds inout arguments definition before the call statement if (InOutArgsValues != null && InOutArgsValues.Length > 0) sbCall.Insert(0, "SET " + string.Join(";SET ", InOutArgsValues) + ";"); if (ArgsValues != null) { foreach (string val in ArgsValues) { sbCall.Append(val).Append(','); } } sbCall.Length--; sbCall.Append(");"); // Parse & create scope CommonTokenStream cts; CommonTree callTree = (CommonTree)(ParseSql(sbCall.ToString(), false, out sb, out cts).Tree); if (callTree.IsNil) callTree = (CommonTree)callTree.GetChild(0); _scope.Push( new RoutineScope() { OwningRoutine = new RoutineInfo() { ParsedTree = callTree, Name = "<main>", TokenStream = cts, SourceCode = sbCall.ToString() }, Variables = new Dictionary<string, StoreType>() }); }
private RoutineInfo MakeRoutineInfo(CommonTree t, CommonTokenStream cts, string sql) { string name = Debugger.GetRoutineName(t); RoutineInfo ri = new RoutineInfo() { Name = name, SourceCode = sql, ParsedTree = t, Type = RoutineInfoType.Procedure, TokenStream = cts }; return ri; }
internal void RegisterRoutine(string sName, RoutineInfo r) { _preinstrumentedRoutines.Add(sName, r); }
private void AddToPreinstrumentedRoutines(RoutineInfo ri) { _preinstrumentedRoutines.Add(string.Format("{0}", ri.GetFullName(_connection.Database)), ri); }
private void RegisterNewOldVars(RoutineInfo ri, Dictionary<string, StoreType> vars) { string sql = string.Format( @"select column_name, data_type, numeric_precision, numeric_scale, character_maximum_length, column_type from information_schema.columns where table_name = '{0}' and table_schema = '{1}'", ri.TriggerInfo.Table, ri.TriggerInfo.ObjectSchema); MySqlCommand cmd = new MySqlCommand(sql, _utilCon); MySqlDataReader r = cmd.ExecuteReader(); try { while (r.Read()) { StoreType st; if( ( ri.TriggerInfo.Event == TriggerEvent.Insert ) || ( ri.TriggerInfo.Event == TriggerEvent.Update )) { st = GetStoreType(r, "new"); vars.Add(st.Name, st); } if ((ri.TriggerInfo.Event == TriggerEvent.Delete) || (ri.TriggerInfo.Event == TriggerEvent.Update)) { st = GetStoreType(r, "old"); vars.Add(st.Name, st); } } } finally { r.Close(); } }
private RoutineScope GetRoutineScopeFromRoutineInfo(RoutineInfo ri) { RoutineScope rs = new RoutineScope() { OwningRoutine = ri, Variables = new Dictionary<string,StoreType>() }; foreach (StoreType st in ri.Locals.Values) { rs.Variables.Add(st.Name, new StoreType(st)); } return rs; }
private void GenerateInstrumentedCodeRecursive( IList<ITree> children, RoutineInfo routine, StringBuilder sql) { IToken tkTmp; ITree trTmp; CommonTokenStream tokenStream = routine.TokenStream; string PreInstrumentationCode = routine.PreInstrumentationCode; string PostInstrumentationCode = routine.PostInstrumentationCode; // The following statements are qualified to have "statement lists" // begin_end, if, while, repeat, loop, declare condition, declare handler, // case's when, then, else. if (children == null) return; foreach (ITree ic in children) { CommonTree tc = (CommonTree)ic; if( !routine._endOfDeclare && ( routine.BeginEnd.Children == children )) { if ((Cmp(tc.Text, "declare") != 0) && (Cmp(tc.Text, "declare_handler") != 0) ) { routine._endOfDeclare = true; // Emit begin scope code EmitBeginScopeCode(sql, routine); } } switch (tc.Text.ToLowerInvariant()) { case "begin_end": { if (Cmp(tc.GetChild(0).Text, "label") == 0) { sql.Append(tc.GetChild(0).GetChild(0).Text).Append(" : "); } sql.AppendLine("begin"); GenerateInstrumentedCodeRecursive(tc.Children, routine, sql); tkTmp = tokenStream.GetTokens(tc.TokenStopIndex, tc.TokenStopIndex).Last(); EmitInstrumentationCode(sql, routine, tkTmp.Line, tkTmp.CharPositionInLine); // TODO: Add END token to AST, so you can put breakpoints in the END //routine.RegisterStatement(tc); sql.AppendLine("end;"); } break; case "if": { int i = 0, j = 0; int idxThen = -1; EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); routine.RegisterStatement(tc); do { while (i < tc.ChildCount && ((Debugger.Cmp(tc.GetChild(i).Text, "if_cond") != 0) && (Debugger.Cmp(tc.GetChild(i).Text, "elseif") != 0))) i++; if (i == tc.ChildCount) break; CommonTree child = (CommonTree)tc.GetChild(i); j = 0; while (j < tc.ChildCount && Debugger.Cmp(child.GetChild(j).Text, "then") != 0) j++; idxThen = j; CommonTree thenTree = (CommonTree)child.GetChild(idxThen); CommonTree exprTree = (CommonTree)child.GetChild(idxThen - 1); // Concat "elseif/if ... then" if (Cmp(child.Text, "if_cond") == 0) sql.Append("if "); else sql.Append("elseif "); ConcatTokens(sql, tokenStream, exprTree.TokenStartIndex, exprTree.TokenStopIndex, true ); sql.AppendLine(" then "); GenerateInstrumentedCodeRecursive(thenTree.Children, routine, sql); i++; } while (true); // look for else i = 0; while (i < tc.ChildCount && Debugger.Cmp(tc.GetChild(i).Text, "else") != 0) i++; if (i < tc.ChildCount) { CommonTree elseTree = (CommonTree)tc.GetChild(i); // concat "else" ConcatTokens(sql, tokenStream, elseTree.TokenStartIndex, elseTree.GetChild(0).TokenStartIndex - 1); GenerateInstrumentedCodeRecursive(elseTree.Children, routine, sql); } sql.AppendLine("end if;"); } break; case "while": { EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); bool hasLabel = false; if (Cmp(tc.GetChild(0).Text, "label") == 0) { sql.Append(tc.GetChild(0).GetChild(0).Text).Append(" : "); hasLabel = true; } // Concat "while ... do" sql.Append(" while "); ConcatTokens(sql, tokenStream, tc.GetChild( 0 ).TokenStartIndex, tc.GetChild(0).TokenStopIndex, false); sql.Append(" do "); if (!hasLabel) { GenerateInstrumentedCodeRecursive( // skip while condition tc.Children.Skip(1).ToList(), routine, sql); } else { GenerateInstrumentedCodeRecursive( // skip while condition & label tc.Children.Skip(2).ToList(), routine, sql); } sql.AppendLine("end while;"); } break; case "repeat": { bool hasLabel = false; if (Cmp(tc.GetChild(0).Text, "label") == 0) { sql.Append(tc.GetChild(0).GetChild(0).Text).Append(" : "); hasLabel = true; } IList<ITree> childColl = tc.Children; sql.AppendLine("repeat"); if (!hasLabel) { GenerateInstrumentedCodeRecursive( // skip until & until-condition childColl.Take(childColl.Count - 2).ToList(), routine, sql); } else { GenerateInstrumentedCodeRecursive( // skip label, until & until-condition childColl.Take(childColl.Count - 2).Skip(1).ToList(), routine, sql); } trTmp = tc.GetChild(tc.ChildCount - 1); EmitInstrumentationCode(sql, routine, trTmp.Line, trTmp.CharPositionInLine); sql.Append("until "); // Concat until condition ConcatTokens(sql, tokenStream, childColl[childColl.Count - 1].TokenStartIndex, childColl[childColl.Count - 1].TokenStopIndex, true ); sql.AppendLine( " end repeat; " ); } break; case "loop": { bool hasLabel = false; if (Cmp(tc.GetChild(0).Text, "label") == 0) { sql.Append(tc.GetChild(0).GetChild(0).Text).Append(" : "); hasLabel = true; } sql.AppendLine("loop"); if (hasLabel) { // skip label children GenerateInstrumentedCodeRecursive(tc.Children.Skip(1).ToList(), routine, sql); } else { GenerateInstrumentedCodeRecursive(tc.Children, routine, sql); } sql.AppendLine("end loop;"); } break; case "declare_handler": { CommonTree beginEnd = (CommonTree)tc.GetChild(2); // Concat declare handler ... (until before begin) ConcatTokens(sql, tokenStream, tc.TokenStartIndex, beginEnd.TokenStartIndex - 1); if (Cmp("begin_end", beginEnd.Text) != 0) { // if there is no begin/end block, need to rewrite code so it appears to be one. List<ITree> nodes = new List<ITree>(); nodes.Add(beginEnd); sql.AppendLine(" begin "); GenerateInstrumentedCodeRecursive(nodes, routine, sql); sql.AppendLine(" end; "); } else { GenerateInstrumentedCodeRecursive( tc.Children.Skip( 2 ).ToList(), routine, sql ); } } break; case "case_stmt": { int i = 0; routine.RegisterStatement(tc); EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); sql.AppendLine("case"); CommonTree whenCt = (CommonTree)tc.GetChild(0); if (Cmp(whenCt.Text, "when") != 0) { CommonTree firstCt = whenCt; ConcatTokens(sql, tokenStream, firstCt.TokenStartIndex, firstCt.TokenStopIndex ); whenCt = (CommonTree)tc.GetChild(1); } while (Cmp(whenCt.Text, "when") == 0) { CommonTree whenExpr = ( CommonTree )whenCt.GetChild( 0 ); sql.Append(" when "); ConcatTokens(sql, tokenStream, whenExpr.TokenStartIndex, whenExpr.TokenStopIndex, false); sql.Append(" then "); GenerateInstrumentedCodeRecursive( whenCt.Children.Skip( 1 ).ToList(), routine, sql ); if (++i >= tc.ChildCount) break; whenCt = (CommonTree)tc.GetChild(i); } if (i < tc.ChildCount) { CommonTree elseCt = (CommonTree)tc.GetChild(tc.ChildCount - 1); sql.Append(" else"); GenerateInstrumentedCodeRecursive( elseCt.Children, routine, sql); } sql.AppendLine("end case;"); } break; case "call": { routine.RegisterStatement(tc); EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); ConcatTokens(sql, tokenStream, tc.TokenStartIndex, tc.TokenStopIndex); // Workaround: sometimes last token of declare statement is not the expected semicolon, if so, add it. if (Cmp(tokenStream.Get(tc.TokenStopIndex).Text, ";") != 0) { sql.Append(';'); } // end workaround } break; case "label": // nothing break; case "leave": { routine.RegisterStatement(tc); string label = tc.GetChild(0).Text; EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); if (routine._leaveLabel == label) { EmitEndScopeCode(sql); } ConcatTokens(sql, tokenStream, tc.TokenStartIndex, tc.TokenStopIndex); } break; case "return": { routine.RegisterStatement(tc); EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); EmitEndScopeCode(sql); ConcatTokens(sql, tokenStream, tc.TokenStartIndex, tc.TokenStopIndex); // Workaround: sometimes last token of declare statement is not the expected semicolon, if so, add it. if (Cmp(tokenStream.Get(tc.TokenStopIndex).Text, ";") != 0) { sql.Append(';'); } // end workaround sql.AppendLine(); } break; default: // not compound code, skip over local variables. if (Cmp(tc.Text, "declare") != 0) { routine.RegisterStatement(tc); EmitInstrumentationCode(sql, routine, tc.Line, tc.CharPositionInLine); ConcatTokens(sql, tokenStream, tc.TokenStartIndex, tc.TokenStopIndex); } else { ConcatTokens(sql, tokenStream, tc.TokenStartIndex, tc.TokenStopIndex); } // Workaround: sometimes last token of declare statement is not the expected semicolon, if so, add it. if (Cmp(tokenStream.Get(tc.TokenStopIndex).Text, ";") != 0) { sql.Append(';'); } // end workaround sql.AppendLine(); break; } } }
private void EmitBeginScopeCode( StringBuilder sql, RoutineInfo ri ) { sql.AppendFormat("if {0} = 0 then ", VAR_DBG_NO_DEBUGGING); sql.AppendLine(); sql.Append("update `serversidedebugger`.`DebugData` set `serversidedebugger`.`DebugData`.`Val` = `serversidedebugger`.`DebugData`.`Val` + 1 where `serversidedebugger`.`DebugData`.`Id` = 1;"); sql.AppendLine(); sql.AppendFormat("call `serversidedebugger`.`Push`( {0}, '{1}' );", DebugSessionId, ri.FullName ); sql.AppendLine(); sql.Append("end if;"); sql.AppendLine(); }
private void EmitInstrumentationCode(StringBuilder sql, RoutineInfo routine, int lineno, int colno) { sql.AppendFormat("if {0} = 0 then ", VAR_DBG_NO_DEBUGGING); sql.AppendLine(); sql.AppendFormat(routine.PreInstrumentationCode, VAR_DBG_SCOPE_LEVEL, lineno, routine.FullName, DebugSessionId, colno); sql.AppendLine(string.Format("call `serversidedebugger`.`ExitEnterCriticalSection`( '{0}', {1} );", routine.Name, lineno )); sql.AppendFormat(routine.PostInstrumentationCode, VAR_DBG_SCOPE_LEVEL, DebugSessionId); sql.AppendLine(" end if;"); }
private void GenerateInstrumentedCode( RoutineInfo routine, StringBuilder sql) { // Get initial begin_end, // TODO: there may be no begin/end. CommonTree t = routine.ParsedTree; CommonTree beginEnd = null; int i = 0; while (i < t.ChildCount && Debugger.Cmp(t.GetChild(i).Text, "begin_end") != 0) i++; beginEnd = (CommonTree)t.GetChild(i); routine.BeginEnd = beginEnd; // generate stub method "create proc... begin" ConcatTokens(sql, routine.TokenStream, t.TokenStartIndex, beginEnd.TokenStartIndex - 1); if( Cmp( beginEnd.GetChild( 0 ).Text, "label" ) == 0 ) { routine._leaveLabel = beginEnd.GetChild(0).GetChild(0).Text; } // Generate scope entry: if (!string.IsNullOrEmpty(routine._leaveLabel)) { sql.Append(routine._leaveLabel).Append(" : "); } sql.AppendLine( "begin" ); sql.AppendLine("#begin scope"); // Generate recursive code: routine._endOfDeclare = false; GenerateInstrumentedCodeRecursive(beginEnd.Children, routine, sql); // Generate scope exit: if (routine.Type != RoutineInfoType.Function) { IToken tk = routine.TokenStream.Get(beginEnd.TokenStopIndex); EmitInstrumentationCode(sql, routine, tk.Line, tk.CharPositionInLine); } sql.AppendLine("#end scope"); if (routine.Type != RoutineInfoType.Function) { // Generate code to cleanup scope. EmitEndScopeCode(sql); } sql.AppendLine("end;"); }
/// <summary> /// Instruments a routine. /// </summary> /// <param name="routine"></param> private void InstrumentRoutine( RoutineInfo routine ) { /* * - Parse formal arguments. * - Parse begin block. * - For each begin block parse declare's, then instructions. * - When finish parsing tree, make a backup. * - Then instrument it. * */ // Parse args StringBuilder sb = new StringBuilder(); CommonTokenStream tokenStream; MySQL51Parser.program_return tree = ParseSql(routine.SourceCode, false, out sb, out tokenStream); routine.ParsedTree = ( CommonTree )( tree.Tree ); if (routine.ParsedTree.IsNil) { routine.ParsedTree = ( CommonTree )routine.ParsedTree.GetChild(0); } routine.TokenStream = tokenStream; Dictionary<string, StoreType> args = ParseArgs( routine.ParsedTree ); // Parse Begin block CommonTree beginEnd = GetBeginEnd(routine.ParsedTree); // There won't be always a begin/end block. if (beginEnd == null) { // begin/end block StringBuilder sbNewRoutineSql = new StringBuilder(); ITree lastChild = routine.ParsedTree.GetChild(routine.ParsedTree.ChildCount - 1); ConcatTokens(sbNewRoutineSql, routine.TokenStream, 0, lastChild.TokenStartIndex - 1); sbNewRoutineSql.AppendLine(); sbNewRoutineSql.AppendLine("begin"); ConcatTokens(sbNewRoutineSql, routine.TokenStream, lastChild.TokenStartIndex, lastChild.TokenStopIndex); if ( Cmp( routine.TokenStream.Get( lastChild.TokenStopIndex ).Text, ";" ) != 0 ) { sbNewRoutineSql.Append(';'); } sbNewRoutineSql.AppendLine(); sbNewRoutineSql.AppendLine( "end" ); StringBuilder sbErrors; tokenStream = null; string sSql = sbNewRoutineSql.ToString(); tree = ParseSql(sSql, false, out sbErrors, out tokenStream); routine.TokenStream = tokenStream; routine.SourceCode = sSql; routine.ParsedTree = (CommonTree)(tree.Tree); if (routine.ParsedTree.IsNil) { routine.ParsedTree = (CommonTree)routine.ParsedTree.GetChild(0); } // No need to reparse args, just begin/end beginEnd = GetBeginEnd(routine.ParsedTree); } // Get declare variables, sessions, new & old ParseDeclares(beginEnd, args); ParseSessions(tokenStream, args); RegisterInternalVars(args); if (routine.Type == RoutineInfoType.Trigger) { RegisterNewOldVars(routine, args); } // forces to backup the routine if (Scope.Count == 0) { RoutineScope tempScope = new RoutineScope(); tempScope.OwningRoutine = routine; tempScope.GetFileName(); } else CurrentScope.GetFileName(); // generate instrumentation code StringBuilder preInscode = new StringBuilder(); // track internal variables... // Workaround: row_count() is affected by any precious SET statement, so must be first preInscode.AppendFormat("set {0} = row_count();", VAR_DBG_ROW_COUNT); preInscode.AppendLine(); preInscode.AppendFormat("set {0} = last_insert_id();", VAR_DBG_LAST_INSERT_ID); preInscode.AppendLine(); preInscode.AppendFormat("set {0} = found_rows();", VAR_DBG_FOUND_ROWS); preInscode.AppendLine(); preInscode.Append(" call `serversidedebugger`.`SetDebugScopeVar`( {3}, {0}, '@@@lineno', {1} );" ).AppendLine(); preInscode.Append(" call `serversidedebugger`.`SetDebugScopeVar`( {3}, {0}, '@@@colno', {4} );").AppendLine(); // ...and user variables foreach (StoreType st in args.Values) { if (st.VarKind == VarKindEnum.Internal) continue; preInscode.AppendLine("call `serversidedebugger`.`SetDebugScopeVar`( {3}, {0}, "). AppendFormat("'{0}', cast( {0} as binary ) );", st.Name).AppendLine(); } StringBuilder postInscode = new StringBuilder(); foreach (StoreType st in args.Values) { if (st.VarKind == VarKindEnum.Internal) continue; postInscode.AppendFormat( @"set {0} = ( select cast( `serversidedebugger`.`DebugScope`.`VarValue` as {1} ) from `serversidedebugger`.`DebugScope` where `serversidedebugger`.`DebugScope`.`DebugSessionId` = {2} ", st.Name, st.GetCastExpressionFromBinary(), DebugSessionId). AppendFormat(" and `serversidedebugger`.`DebugScope`.`DebugScopeLevel` = {0} and `serversidedebugger`.`DebugScope`.`VarName` = '{1}' and `serversidedebugger`.`DebugScope`.`Id` = ( select max( `serversidedebugger`.`DebugScope`.`Id` ) from `serversidedebugger`.`DebugScope` where `serversidedebugger`.`DebugScope`.`DebugSessionId` = {2} and `serversidedebugger`.`DebugScope`.`DebugScopeLevel` = {0} and `serversidedebugger`.`DebugScope`.`VarName` = '{1}' ));", "{0}", st.Name, DebugSessionId ); postInscode.AppendLine(); } routine.PreInstrumentationCode = preInscode.ToString(); routine.PostInstrumentationCode = postInscode.ToString(); routine.Locals = args; // finally instrument. StringBuilder sql = new StringBuilder(); // Instrumeting code... GenerateInstrumentedCode(routine, sql); routine.InstrumentedSourceCode = sql.ToString(); string sqlDrop = string.Format("drop {0} {1}", routine.Type.ToString(), routine.Name); string db = _utilCon.Database; if (!string.IsNullOrEmpty(routine.Schema)) { ExecuteRaw(string.Format("use `{0}`", routine.Schema )); } ExecuteRaw(sqlDrop); try { ExecuteRaw(string.Format("delimiter //\n{0}\n//", routine.InstrumentedSourceCode)); } catch (Exception) { // In case of exception restore previous non-instrumented version. ExecuteRaw(string.Format("delimiter //\n{0}\n//", routine.SourceCode)); throw; } finally { ExecuteRaw(string.Format("use `{0}`", db)); } }