} // end GenerateView() protected override void ApplyViewToInputObject() { for (int idx = 0; idx < m_view.ListItems.Count; idx++) { if (Stopping) { break; } ColorString listItem = new ColorString(sm_labelColors); ListItem li = m_view.ListItems[idx]; listItem.Append(PadAndAlign(li.Label, m_view.MaxLabelLength + 1, ColumnAlignment.Left) + ": "); listItem.Append(sm_pop.ToString(DbgProvider.HostSupportsColor)); string val; if (li is PropertyListItem) { var pli = (PropertyListItem)li; val = RenderPropertyValue(InputObject, pli.PropertyName, pli.FormatString); } else { var sli = (ScriptListItem)li; val = RenderScriptValue(InputObject, sli.Script); if (null == val) { val = String.Empty; } } listItem.Append(_Indent(val)); SafeWriteObject(listItem); } // N.B. Using String.Empty here used to cause 3 blank lines instead of one. // I don't understand precisely why, but the crux of the problem is that // // a) System.String has a custom view definition (which is to get around // PowerShell's reticence to properly display strings if they have // other stuff in their TypeNames) (see commit 4bc7d1c76f97d0) // b) When we write the string here, for some reason it causes a // transition between steppable pipelines, and the PS default // formatter wants to put in an extra newline at the format start and // another at the format end. // // Fortunately, it seems easy enough to workaround by sending a different type // of object down the pipeline. // // (I wonder if it might have been specific to the particular commands I was // using to test, like "uf blah!blah | fl", or symbols | fl, because of how // their formatting was done.) // //SafeWriteObject( String.Empty ); // to get a blank line SafeWriteObject(ColorString.Empty); // to get a blank line } // end ApplyViewToInputObject()
} // end ResetState() private void _WriteHeaders() { ColorString headers = new ColorString(sm_tableHeaderColors); // Write labels: for (int colIdx = 0; colIdx < m_view.Columns.Count; colIdx++) { Column c = m_view.Columns[colIdx]; headers.Append(PadAndAlign(c.Label, c.CalculatedWidth, c.CalculatedAlignment, c.TrimLocation)); if (colIdx != (m_view.Columns.Count - 1)) { headers.Append(" "); // separator } } // Clear the header color: headers.Append(sm_pop); headers.AppendLine(); // Write -------: for (int colIdx = 0; colIdx < m_view.Columns.Count; colIdx++) { Column c = m_view.Columns[colIdx]; int len = CaStringUtil.Length(c.Label); headers.Append(PadAndAlign(new String('-', Math.Min(len, c.CalculatedWidth)), c.CalculatedWidth, c.CalculatedAlignment, c.TrimLocation)); if (colIdx != (m_view.Columns.Count - 1)) { headers.Append(" "); // separator } } SafeWriteObject(headers); } // end _WriteHeaders()
private ColorString FormatRGB(uint numColumns, bool withAlpha) { var bytes = Memory.Bytes; bool is32Bit = Debugger.TargetIs32Bit; ulong startAddress = Memory.StartAddress; if (0 == numColumns) { numColumns = 64; } ColorString cs = new ColorString(); var bytesPerCharacter = withAlpha ? 4 : 3; int bytesPerRow = (int)numColumns * bytesPerCharacter + 3 & ~(3); //round up for (int rowStart = 0; rowStart + bytesPerCharacter < bytes.Count; rowStart += bytesPerRow) { if (rowStart != 0) { cs.AppendLine(); } cs.Append(DbgProvider.FormatAddress(startAddress + (uint)rowStart, is32Bit, true, true)).Append(" "); var rowLen = Math.Min(bytes.Count - rowStart, bytesPerRow); for (int colOffset = 0; colOffset + bytesPerCharacter < rowLen; colOffset += bytesPerCharacter) { byte b = bytes[rowStart + colOffset + 0]; byte g = bytes[rowStart + colOffset + 1]; byte r = bytes[rowStart + colOffset + 2]; string ch = "█"; if (withAlpha) { ch = AlphaChars[bytes[rowStart + colOffset + 3] >> 6]; } cs.AppendFgRgb(r, g, b, ch); } } return(cs.MakeReadOnly()); }
} // end ProcessRecord() private void _UnassembleInstructions(ulong addr, Func <ulong, bool> keepGoing) { ColorString csBlockId; try { ulong disp; var addrName = Debugger.GetNameByOffset(addr, out disp); csBlockId = DbgProvider.ColorizeSymbol(addrName); if (0 != disp) { csBlockId.Append(Util.Sprintf("+{0:x}", disp)); } } catch (DbgEngException) { // Ignore. We'll just use the address as the block id. If we really can't // get the memory there, we'll fail with a good error message later. csBlockId = new ColorString().Append(DbgProvider.FormatAddress(addr, Debugger.TargetIs32Bit, true)); } csBlockId.Append(":").MakeReadOnly(); bool hasCodeBytes = !Debugger.AssemblyOptions.HasFlag(DbgAssemblyOptions.NoCodeBytes); while (keepGoing(addr)) { ulong tmpAddr = addr; string disasm = Debugger.Disassemble(tmpAddr, out addr).Trim(); WriteObject(_ParseDisassembly(tmpAddr, disasm, csBlockId, hasCodeBytes)); } DbgProvider.SetAutoRepeatCommand(Util.Sprintf("{0} -Address 0x{1} -InstructionCount {2}", MyInvocation.InvocationName, addr.ToString("x"), InstructionCount)); NextAddrToDisassemble = addr; } // end _UnassembleInstructions()
} // end EndProcessing() protected override void ApplyViewToInputObject() { if (!m_calculatedWidths) { m_view.CalculateWidthsAndAlignments(Host.UI.RawUI.BufferSize.Width, InputObject); m_calculatedWidths = true; } if (!m_headerShown && !HideTableHeaders) { _WriteHeaders(); m_headerShown = true; } // Write row values: ColorString row = new ColorString(); for (int colIdx = 0; colIdx < m_view.Columns.Count; colIdx++) { if (Stopping) { break; } string val; Column c = m_view.Columns[colIdx]; if (c is PropertyColumn) { PropertyColumn pc = (PropertyColumn)c; val = RenderPropertyValue(InputObject, pc.PropertyName, pc.FormatString); } else { ScriptColumn sc = (ScriptColumn)c; val = RenderScriptValue(InputObject, sc.Script); if (null == val) { val = String.Empty; } } try { val = PadAndAlign(val, c.CalculatedWidth, c.CalculatedAlignment, c.TrimLocation); } catch (Exception e) { // Debugging aid to help you figure out what blew up: e.Data["ColumnLabel"] = c.Label; e.Data["val"] = val; // Sometimes you have a column that's very small (3 or less cells // wide). If you get an error trying to render the value that goes in // it, it will certainly be too wide, but we can't add an ellipsis // when truncating (not enough room). Rather than blow up the // formatting operation, we'll substitute a shorthand to indicate that // an error occurred. if ((e is ArgumentException) && (c.CalculatedWidth <= 3)) { if (c.CalculatedWidth == 3) { val = new ColorString(ConsoleColor.Red, "ERR").ToString(DbgProvider.HostSupportsColor); } else if (c.CalculatedWidth == 2) { val = new ColorString(ConsoleColor.Red, "ER").ToString(DbgProvider.HostSupportsColor); } else if (c.CalculatedWidth == 1) { val = new ColorString(ConsoleColor.Red, "X").ToString(DbgProvider.HostSupportsColor); } else { var msg = Util.Sprintf("Calculated width of 0 or less? {0}", c.CalculatedWidth); Util.Fail(msg); throw new Exception(msg, e); } } else { throw; } } row.Append(val); if (colIdx != (m_view.Columns.Count - 1)) { row.Append(" "); // separator } } SafeWriteObject(row); } // end ApplyViewToInputObject()
// Throws a DbgProviderException if the disassembly represents a bad memory access. internal static DbgDisassembly _ParseDisassembly(ulong address, string s, ColorString blockId, bool hasCodeBytes) { // Example inputs: // // 0113162e 55 push ebp // 0113162f 8bec mov ebp,esp // 01131631 51 push ecx // 01131632 894dfc mov dword ptr [ebp-4],ecx // 01131635 8b45fc mov eax,dword ptr [ebp-4] // 01131638 c70068c81301 mov dword ptr [eax],offset TestNativeConsoleApp!VirtualBase1::`vftable' (0113c868) // 0113163e 8b4508 mov eax,dword ptr [ebp+8] // 01131641 83e001 and eax,1 // 01131644 740a je TestNativeConsoleApp!VirtualBase1::`scalar deleting destructor'+0x22 (01131650) // 01131646 ff75fc push dword ptr [ebp-4] // 01131649 ff1578c01301 call dword ptr [TestNativeConsoleApp!_imp_??3YAXPAXZ (0113c078)] // 0113164f 59 pop ecx // 01131650 8b45fc mov eax,dword ptr [ebp-4] // 01131653 c9 leave // 01131654 c20400 ret 4 // // Here's what it looks like if the address is bad: // // 00007ff6`ece87d60 ?? ??? // ColorString cs = new ColorString(); byte[] codeBytes = null; string instruction = null; string arguments = null; Regex goodRegex; Regex badRegex; if (hasCodeBytes) { goodRegex = sm_asmRegex; badRegex = sm_badAsmRegex; } else { goodRegex = sm_asmRegex_noCodeBytes; badRegex = sm_badAsmRegex_noCodeBytes; } int matchCount = 0; foreach (Match match in goodRegex.Matches(s)) { if (0 == address) { // Then we need to parse it out. (this is for -WholeFunction. if (!DbgProvider.TryParseHexOrDecimalNumber(match.Groups["addr"].Value, out address)) { throw new Exception(Util.Sprintf("Couldn't convert to address: {0}", match.Groups["addr"].Value)); } } #if DEBUG else { ulong parsedAddress; if (!DbgProvider.TryParseHexOrDecimalNumber(match.Groups["addr"].Value, out parsedAddress)) { throw new Exception(Util.Sprintf("Couldn't convert to address: {0}", match.Groups["addr"].Value)); } // Nope: these are routinely different on ARM/THUMB2, where the low // bit of the program counter is used as some sort of flag. //Util.Assert( address == parsedAddress ); } #endif if (hasCodeBytes) { codeBytes = Util.ConvertToBytes(match.Groups["codebytes"].Value); } instruction = match.Groups["instr"].Value; arguments = match.Groups["args"].Value; matchCount++; cs.AppendPushPopFg(ConsoleColor.DarkCyan, match.Groups["addr"].Value) .Append(match.Groups["space1"].Value); if (hasCodeBytes) { cs.AppendPushPopFg(ConsoleColor.DarkGray, match.Groups["codebytes"].Value) .Append(match.Groups["space2"].Value); } var instr = match.Groups["instr"].Value; cs.Append(DbgProvider.ColorizeInstruction(instr)); cs.Append(match.Groups["space3"].Value); cs.Append(match.Groups["args"].Value); } if (0 == matchCount) { var match = badRegex.Match(s); if ((null != match) && match.Success) { string addrString = match.Groups["addr"].Value; if (0 == address) { if (!DbgProvider.TryParseHexOrDecimalNumber(addrString, out address)) { Util.Fail(Util.Sprintf("Couldn't convert to address: {0}", addrString)); } } throw new DbgMemoryAccessException(address, Util.Sprintf("No code found at {0}.", addrString)); } else { throw new Exception(Util.Sprintf("TODO: Need to handle disassembly format: {0}", s)); } } else { Util.Assert(1 == matchCount); } return(new DbgDisassembly(address, codeBytes, instruction, arguments, blockId, cs.MakeReadOnly())); } // end _ParseDisassembly()