| /* |
| * 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. |
| */ |
| |
| package flex.tools.debugger.cli; |
| |
| import java.io.PrintWriter; |
| import java.text.ParseException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import flash.localization.LocalizationManager; |
| import flash.swf.tools.Disassembler; |
| import flash.swf.types.ActionList; |
| import flash.tools.ActionLocation; |
| import flash.tools.debugger.Bootstrap; |
| import flash.tools.debugger.NotConnectedException; |
| import flash.tools.debugger.PlayerDebugException; |
| import flash.tools.debugger.Session; |
| import flash.tools.debugger.SourceFile; |
| import flash.tools.debugger.SuspendReason; |
| import flash.tools.debugger.SuspendedException; |
| import flash.tools.debugger.SwfInfo; |
| import flash.tools.debugger.Value; |
| import flash.tools.debugger.concrete.DMessage; |
| import flash.tools.debugger.concrete.DMessageCounter; |
| import flash.tools.debugger.concrete.DModule; |
| import flash.tools.debugger.concrete.DSuspendInfo; |
| import flash.tools.debugger.concrete.DSwfInfo; |
| import flash.tools.debugger.concrete.PlayerSession; |
| import flash.tools.debugger.concrete.PlayerSessionManager; |
| import flash.util.FieldFormat; |
| |
| /** |
| * Extensions class is a singleton that contains |
| * every cli method that does not conform to the |
| * API. Thus we can easily remove these features |
| * from the cli if the implementation does not |
| * support these calls. |
| */ |
| public class Extensions |
| { |
| public final static String m_newline = System.getProperty("line.separator"); //$NON-NLS-1$ |
| |
| public static void doShowStats(DebugCLI cli) throws IllegalStateException |
| { |
| /* we do some magic casting */ |
| Session session = cli.getSession(); |
| StringBuilder sb = new StringBuilder(); |
| try |
| { |
| PlayerSession p = (PlayerSession)session; |
| DMessageCounter cnt = p.getMessageCounter(); |
| |
| sb.append(getLocalizationManager().getLocalizedTextString("key16")); //$NON-NLS-1$ |
| sb.append(m_newline); |
| for(int i=0; i<=DMessage.InSIZE; i++) |
| { |
| long amt = cnt.getInCount(i); |
| if (amt > 0) |
| { |
| sb.append('\n'); |
| sb.append(DMessage.inTypeName(i)); |
| sb.append(" = "); //$NON-NLS-1$ |
| sb.append(amt); |
| } |
| } |
| |
| sb.append("\n\n"); //$NON-NLS-1$ |
| sb.append(getLocalizationManager().getLocalizedTextString("key17")); //$NON-NLS-1$ |
| sb.append("\n"); //$NON-NLS-1$ |
| for(int i=0; i<=DMessage.OutSIZE; i++) |
| { |
| long amt = cnt.getOutCount(i); |
| if (amt > 0) |
| { |
| sb.append('\n'); |
| sb.append(DMessage.outTypeName(i)); |
| sb.append(" = "); //$NON-NLS-1$ |
| sb.append(amt); |
| } |
| } |
| |
| sb.append('\n'); |
| cli.out( sb.toString() ); |
| } |
| catch(NullPointerException e) |
| { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public static void doShowFuncs(DebugCLI cli) |
| { |
| StringBuilder sb = new StringBuilder(); |
| |
| String arg = null; |
| FileInfoCache fileInfo = cli.getFileCache(); |
| |
| // we take an optional single arg which specifies a module |
| try |
| { |
| if (cli.hasMoreTokens()) |
| { |
| arg = cli.nextToken(); |
| int id = arg.equals(".") ? cli.propertyGet(DebugCLI.LIST_MODULE) : cli.parseFileArg(cli.getActiveIsolateId(), -1, arg); //$NON |
| |
| DModule m = (DModule)fileInfo.getFile(id, cli.getActiveIsolateId()); |
| m.lineMapping(sb); |
| } |
| else |
| { |
| SourceFile[] ar = fileInfo.getFileList(); |
| if (ar == null) |
| cli.err(getLocalizationManager().getLocalizedTextString("key18")); //$NON-NLS-1$ |
| else |
| { |
| for (int i = 0; ar != null && i < ar.length; i++) |
| { |
| DModule m = (DModule)ar[i]; |
| m.lineMapping(sb); |
| } |
| } |
| } |
| |
| cli.out(sb.toString()); |
| } |
| catch(NullPointerException npe) |
| { |
| cli.err(getLocalizationManager().getLocalizedTextString("key19")); //$NON-NLS-1$ |
| } |
| catch(ParseException pe) |
| { |
| cli.err(pe.getMessage()); |
| } |
| catch(AmbiguousException ae) |
| { |
| cli.err(ae.getMessage()); |
| } |
| catch(NoMatchException nme) |
| { |
| cli.err(nme.getMessage()); |
| } |
| } |
| |
| /** |
| * Dump the content of internal variables |
| */ |
| public static void doShowProperties(DebugCLI cli) |
| { |
| StringBuilder sb = new StringBuilder(); |
| |
| Session session = cli.getSession(); |
| for (String key: cli.propertyKeys()) |
| { |
| int value = cli.propertyGet(key); |
| sb.append(key); |
| sb.append(" = "); //$NON-NLS-1$ |
| sb.append(value); |
| sb.append('\n'); |
| } |
| |
| // session manager |
| { |
| PlayerSessionManager mgr = (PlayerSessionManager)Bootstrap.sessionManager(); |
| sb.append(getLocalizationManager().getLocalizedTextString("key21")); //$NON-NLS-1$ |
| sb.append('\n'); |
| for (String key: mgr.keySet()) |
| { |
| Object value = mgr.getPreferenceAsObject(key); |
| sb.append(key); |
| sb.append(" = "); //$NON-NLS-1$ |
| sb.append(value); |
| sb.append('\n'); |
| } |
| } |
| |
| if (session != null) |
| { |
| PlayerSession psession = (PlayerSession)session; |
| sb.append(getLocalizationManager().getLocalizedTextString("key22")); //$NON-NLS-1$ |
| sb.append('\n'); |
| for (String key: psession.keySet()) |
| { |
| Object value = psession.getPreferenceAsObject(key); |
| sb.append(key); |
| sb.append(" = "); //$NON-NLS-1$ |
| sb.append(value); |
| sb.append('\n'); |
| } |
| } |
| |
| cli.out( sb.toString() ); |
| } |
| |
| /** |
| * Dump the break reason and offset |
| */ |
| public static void doShowBreak(DebugCLI cli) throws NotConnectedException |
| { |
| int isolateId = cli.getActiveIsolateId(); |
| cli.waitTilHalted(isolateId); |
| try |
| { |
| Session session = cli.getSession(); |
| StringBuilder sb = new StringBuilder(); |
| if (session.getWorkerSession(isolateId).isSuspended()) |
| { |
| sb.append(getLocalizationManager().getLocalizedTextString("stopped")); //$NON-NLS-1$ |
| sb.append(' '); |
| appendBreakInfo(cli, sb, true, isolateId); |
| } |
| else |
| sb.append(getLocalizationManager().getLocalizedTextString("key24")); //$NON-NLS-1$ |
| |
| cli.out( sb.toString() ); |
| } |
| catch(NullPointerException npe) |
| { |
| cli.err(getLocalizationManager().getLocalizedTextString("key25")); //$NON-NLS-1$ |
| } |
| } |
| |
| // Extended low level break information |
| public static void appendBreakInfo(DebugCLI cli, StringBuilder sb, boolean includeFault, int isolateId) throws NotConnectedException |
| { |
| Session session = cli.getSession(); |
| FileInfoCache fileInfo = cli.getFileCache(); |
| |
| int reason = session.suspendReason(); |
| int offset = ((PlayerSession)session).getSuspendOffset(); |
| int index = ((PlayerSession)session).getSuspendActionIndex(); |
| |
| SwfInfo info = null; |
| try { info = fileInfo.getSwfs(isolateId)[index]; } catch(ArrayIndexOutOfBoundsException oobe) {} |
| if (info != null) |
| { |
| Map<String, String> args = new HashMap<String, String>(); |
| args.put("swfName", FileInfoCache.nameOfSwf(info) ); //$NON-NLS-1$ |
| sb.append(getLocalizationManager().getLocalizedTextString("key35", args)); //$NON-NLS-1$ |
| sb.append(' '); |
| } |
| |
| Map<String, String> args = new HashMap<String, String>(); |
| args.put("address", "0x" + FieldFormat.formatLongToHex(new StringBuilder(), offset, 8) + " (" + offset + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| sb.append(getLocalizationManager().getLocalizedTextString("atAddress", args)); //$NON-NLS-1$ |
| |
| if (includeFault) |
| { |
| args = new HashMap<String, String>(); |
| StringBuilder reasonBuffer = new StringBuilder(); |
| cli.appendReason(reasonBuffer, reason); |
| args.put("fault", reasonBuffer.toString() ); //$NON-NLS-1$ |
| sb.append(' '); |
| sb.append(getLocalizationManager().getLocalizedTextString("haltedDueToFault", args)); //$NON-NLS-1$ |
| } |
| } |
| |
| // Raw direct call to Player |
| public static void doShowVariable(DebugCLI cli) throws PlayerDebugException |
| { |
| int isolateId = cli.getActiveIsolateId(); |
| cli.waitTilHalted(isolateId); |
| try |
| { |
| // an integer followed by a variable name |
| Session session = cli.getSession(); |
| long id = cli.nextLongToken(); |
| String name = (cli.hasMoreTokens()) ? cli.nextToken() : null; |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append(name); |
| sb.append(" = "); //$NON-NLS-1$ |
| Value v = ((PlayerSession)session).getValue(id, name, isolateId); |
| cli.m_exprCache.appendVariableValue(sb, v, isolateId); |
| cli.out( sb.toString() ); |
| } |
| catch(NullPointerException npe) |
| { |
| cli.err(getLocalizationManager().getLocalizedTextString("key26")); //$NON-NLS-1$ |
| } |
| } |
| |
| public static void doDisassemble(DebugCLI cli) throws PlayerDebugException |
| { |
| /* currentXXX may NOT be invalid! */ |
| int currentModule = cli.propertyGet(DebugCLI.LIST_MODULE); |
| int currentLine = cli.propertyGet(DebugCLI.LIST_LINE); |
| int currentIsolate = cli.propertyGet(DebugCLI.LIST_WORKER); |
| |
| String arg1 = null; |
| int module1 = currentModule; |
| int line1 = currentLine; |
| |
| String arg2 = null; |
| int line2 = currentLine; |
| |
| boolean functionNamed = false; |
| int numLines = 0; |
| try |
| { |
| FileInfoCache fileInfo = cli.getFileCache(); |
| Session session = cli.getSession(); |
| int isolateId = cli.getActiveIsolateId(); |
| if (cli.hasMoreTokens()) |
| { |
| arg1 = cli.nextToken(); |
| if (arg1.equals("-")) //$NON-NLS-1$ |
| { |
| // move back one line |
| line1 = line2 = line1 - 1; |
| } |
| else |
| { |
| int wasFunc = 0; |
| |
| FileLocation[] fileLocations = cli.parseLocationArg(currentModule, currentLine, arg1, false); |
| |
| if (fileLocations.length == 1) { |
| module1 = fileLocations[0].getModule(); |
| line2 = line1 = fileLocations[0].getLine(); |
| functionNamed = (fileLocations[0].getWasFunc() != 0); |
| } |
| |
| if (cli.hasMoreTokens()) { |
| arg2 = cli.nextToken(); |
| line2 = cli.parseLineArg(module1, arg2); |
| } |
| } |
| } |
| else |
| { |
| // since no parms test for valid location if none use players concept of where we stopped |
| if( fileInfo.getFile(currentModule, currentIsolate) == null) |
| { |
| //here we simply use the players concept of suspsend |
| DSuspendInfo info = ((PlayerSession)session).getSuspendInfoIsolate(isolateId); |
| int at = info.getOffset(); |
| int which = info.getActionIndex(); |
| int until = info.getNextOffset(); |
| if (info.getReason() == SuspendReason.Unknown) |
| throw new SuspendedException(); |
| |
| SwfInfo swf = fileInfo.getSwfs(isolateId)[which]; |
| outputAssembly(cli, (DSwfInfo)swf, at, until); |
| throw new AmbiguousException(getLocalizationManager().getLocalizedTextString("key27")); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Check for a few error conditions, otherwise we'll write a listing! |
| */ |
| if (cli.hasMoreTokens()) |
| { |
| cli.err(getLocalizationManager().getLocalizedTextString("key28")); //$NON-NLS-1$ |
| } |
| else |
| { |
| SourceFile file = fileInfo.getFile(module1); |
| numLines = file.getLineCount(); |
| |
| // pressing return is ok, otherwise throw the exception |
| if (line1 > numLines && arg1 != null) |
| throw new IndexOutOfBoundsException(); |
| |
| /* if no arg2 then user list a single line */ |
| if (arg2 == null) |
| line2 = line1; |
| |
| /* adjust our range of lines to ensure we conform */ |
| if (line1 < 1) |
| { |
| /* shrink line 1, grow line2 */ |
| line2 += -(line1 - 1); |
| line1 = 1; |
| } |
| |
| if (line2 > numLines) |
| line2 = numLines; |
| |
| // System.out.println("1="+module1+":"+line1+",2="+module2+":"+line2+",num="+numLines+",half="+half); |
| |
| /* nothing to display */ |
| if (line1 > line2) |
| throw new IndexOutOfBoundsException(); |
| |
| /* now dump the mixed source / assembly */ |
| // now lets find which swf this in |
| DSwfInfo swf = (DSwfInfo)fileInfo.swfForFile(file, cli.getActiveIsolateId()); |
| ActionLocation lStart = null; |
| ActionLocation lEnd = null; |
| |
| if (swf == null) |
| { |
| Map<String, String> args = new HashMap<String, String>(); |
| args.put("arg3", file.getName()); //$NON-NLS-1$ |
| cli.err(getLocalizationManager().getLocalizedTextString("key29", args)); //$NON-NLS-1$ |
| } |
| else if (functionNamed) |
| { |
| // if we name a function just dump the whole thing without source. |
| int offset = file.getOffsetForLine(line1); |
| lStart = swf.locate(offset); |
| if (lStart.function == null) |
| cli.err(getLocalizationManager().getLocalizedTextString("key30")); //$NON-NLS-1$ |
| else |
| { |
| // create a psudeo action list from which to disasemble the function |
| ActionList al = new ActionList(true); |
| al.setActionOffset(0, lStart.function); |
| lStart.actions = al; |
| lStart.at = 0; |
| lEnd = new ActionLocation(); |
| lEnd.actions = al; |
| lEnd.at = 0; |
| outputAssembly(cli, swf, lStart, lEnd); |
| } |
| } |
| else |
| { |
| ActionLocation lastEnd = null; |
| for(int i=line1; i<=line2; i++) |
| { |
| int offset = file.getOffsetForLine(i); |
| |
| // locate the action list associated with this of the swf |
| if (offset != 0) |
| { |
| // get the starting point and try to locate a nice ending |
| lStart = swf.locate(offset); |
| lEnd = swf.locateSourceLineEnd(lStart); |
| |
| // now see if we skipped some assembly between source lines |
| if (lastEnd != null) |
| { |
| lastEnd.at++; // point our pseudo start to the next action |
| |
| // new actions list so attempt to find the end of source in the old actions list |
| if (lastEnd.actions != lStart.actions && lastEnd.actions.size() != lastEnd.at) |
| { |
| String atString = Integer.toHexString(lastEnd.actions.getOffset(lastEnd.at)); |
| Map<String, String> args = new HashMap<String, String>(); |
| args.put("arg4", atString); //$NON-NLS-1$ |
| cli.out(getLocalizationManager().getLocalizedTextString("key31", args)); //$NON-NLS-1$ |
| |
| // we are missing some of the dissassembly, so back up a bit and dump it out |
| ActionLocation gapEnd = swf.locateSourceLineEnd(lastEnd); |
| outputAssembly(cli, swf, lastEnd, gapEnd); |
| } |
| else if (lastEnd.at < lStart.at) |
| { |
| // same action list but we skipped some instructions |
| ActionLocation gapEnd = new ActionLocation(lStart); |
| gapEnd.at--; |
| outputAssembly(cli, swf, lastEnd, gapEnd); |
| } |
| } |
| lastEnd = lEnd; |
| } |
| |
| // dump source |
| cli.outputSource(module1, i, file.getLine(i)); |
| |
| // obtain the offset, locate it in the swf |
| if (offset != 0) |
| outputAssembly(cli, swf, lStart, lEnd); |
| } |
| |
| /* save away valid context */ |
| cli.propertyPut(DebugCLI.LIST_MODULE, module1); |
| cli.propertyPut(DebugCLI.LIST_LINE, line2 + 1); // add one |
| cli.m_repeatLine = "disassemble"; /* allow repeated listing by typing CR */ //$NON-NLS-1$ |
| } |
| } |
| } |
| catch(IndexOutOfBoundsException iob) |
| { |
| String name = "#"+module1; //$NON-NLS-1$ |
| Map<String, String> args = new HashMap<String, String>(); |
| args.put("arg5", Integer.toString(line1)); //$NON-NLS-1$ |
| args.put("arg6", name); //$NON-NLS-1$ |
| args.put("arg7", Integer.toString(numLines)); //$NON-NLS-1$ |
| cli.err(getLocalizationManager().getLocalizedTextString("key32", args)); //$NON-NLS-1$ |
| } |
| catch(AmbiguousException ae) |
| { |
| cli.err(ae.getMessage()); |
| } |
| catch(NullPointerException npe) |
| { |
| cli.err(getLocalizationManager().getLocalizedTextString("key33")); //$NON-NLS-1$ |
| } |
| catch(ParseException pe) |
| { |
| cli.err(pe.getMessage()); |
| } |
| catch(NoMatchException nme) |
| { |
| cli.err(nme.getMessage()); |
| } |
| catch(SuspendedException se) |
| { |
| cli.err(getLocalizationManager().getLocalizedTextString("key34")); //$NON-NLS-1$ |
| } |
| } |
| |
| private static LocalizationManager getLocalizationManager() |
| { |
| return DebugCLI.getLocalizationManager(); |
| } |
| |
| /** |
| * Disassemble part of the swf to the output |
| */ |
| public static ActionLocation outputAssembly(DebugCLI cli, DSwfInfo swf, int start, int end) |
| { |
| // first we need to locate the action list associated with this |
| // portion of the swf |
| ActionLocation lStart = swf.locate(start); |
| ActionLocation lEnd = (end > -1) ? swf.locate(end) : swf.locateSourceLineEnd(lStart); |
| |
| return outputAssembly(cli, swf, lStart, lEnd); |
| } |
| |
| public static ActionLocation outputAssembly(DebugCLI cli, SwfInfo info, ActionLocation lStart, ActionLocation lEnd) |
| { |
| // now make sure our actions lists are the same (i.e we haven't spanned past one tag) |
| if (lStart.actions != lEnd.actions) |
| lEnd.at = lStart.actions.size()-1; |
| |
| Disassembler.disassemble(lStart.actions, lStart.pool, lStart.at, lEnd.at, new PrintWriter(cli.getOut())); |
| return lEnd; |
| } |
| } |