/// <summary> /// Generate debug info for this entry. /// </summary> public override void Generate(DebugInfo info, ref int lastLine, ref int lastAddress, ref string lastUrl, ref bool firstPositionEntry, HashSet<Register> startedVariables) { var documentChanged = false; if (!string.IsNullOrEmpty(url) && (url != lastUrl)) { lastUrl = url; info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.SetFile, lastUrl)); documentChanged = true; } var lineAdv = line - lastLine; var offsetAdv = Offset - lastAddress; if ((lineAdv != 0) || documentChanged || firstPositionEntry) { // Set position entry DebugOpCodes opcode; if (!TryCalculateSpecialOpcode(lineAdv, offsetAdv, out opcode)) { // The line and/or offset advance does not fit in the special opcode, use ADVANCE opcodes if (lineAdv != 0) info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.AdvanceLine, lineAdv)); if (offsetAdv != 0) info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.AdvancePc, offsetAdv)); TryCalculateSpecialOpcode(0, 0, out opcode); } info.DebugInstructions.Add(new DebugInstruction(opcode)); lastLine = line; lastAddress = Offset; firstPositionEntry = false; } }
/// <summary> /// Generate debug info for this entry. /// </summary> public override void Generate(DebugInfo info, ref int lastLine, ref int lastAddress, ref string lastUrl, ref bool firstPositionEntry, HashSet<Register> startedVariables) { var offsetAdv = Offset - lastAddress; if (offsetAdv > 0) { info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.AdvancePc, offsetAdv)); lastAddress = Offset; } info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.EndLocal, register)); }
private void ReadDebugInfo(BinaryReader reader, MethodDefinition mdef, InstructionReader instructionReader, uint debugOffset) { reader.PreserveCurrentPosition(debugOffset, () => { var debugInfo = new DebugInfo(mdef.Body); mdef.Body.DebugInfo = debugInfo; uint lineStart = reader.ReadULEB128(); debugInfo.LineStart = lineStart; uint parametersSize = reader.ReadULEB128(); for (int i = 0; i < parametersSize; i++) { long index = reader.ReadULEB128p1(); string name = null; if (index != DexConsts.NoIndex && index >= 0) name = strings[(int) index]; debugInfo.Parameters.Add(name); } while (true) { var ins = new DebugInstruction((DebugOpCodes) reader.ReadByte()); debugInfo.DebugInstructions.Add(ins); uint registerIndex; uint addrDiff; long nameIndex; long typeIndex; long signatureIndex; int lineDiff; string name; switch (ins.OpCode) { case DebugOpCodes.AdvancePc: // uleb128 addr_diff addrDiff = reader.ReadULEB128(); ins.Operands.Add(addrDiff); break; case DebugOpCodes.AdvanceLine: // sleb128 line_diff lineDiff = reader.ReadSLEB128(); ins.Operands.Add(lineDiff); break; case DebugOpCodes.EndLocal: case DebugOpCodes.RestartLocal: // uleb128 register_num registerIndex = reader.ReadULEB128(); ins.Operands.Add(mdef.Body.Registers[(int) registerIndex]); break; case DebugOpCodes.SetFile: // uleb128p1 name_idx nameIndex = reader.ReadULEB128p1(); name = null; if (nameIndex != DexConsts.NoIndex && nameIndex >= 0) name = strings[(int) nameIndex]; ins.Operands.Add(name); break; case DebugOpCodes.StartLocalExtended: case DebugOpCodes.StartLocal: // StartLocalExtended : uleb128 register_num, uleb128p1 name_idx, uleb128p1 type_idx, uleb128p1 sig_idx // StartLocal : uleb128 register_num, uleb128p1 name_idx, uleb128p1 type_idx Boolean isExtended = ins.OpCode == DebugOpCodes. StartLocalExtended; registerIndex = reader.ReadULEB128(); ins.Operands.Add(mdef.Body.Registers[(int) registerIndex]); nameIndex = reader.ReadULEB128p1(); name = null; if (nameIndex != DexConsts.NoIndex && nameIndex >= 0) name = strings[(int) nameIndex]; ins.Operands.Add(name); typeIndex = reader.ReadULEB128p1(); TypeReference type = null; if (typeIndex != DexConsts.NoIndex && typeIndex >= 0) type = typeReferences[(int) typeIndex]; ins.Operands.Add(type); if (isExtended) { signatureIndex = reader.ReadULEB128p1(); string signature = null; if (signatureIndex != DexConsts.NoIndex && signatureIndex >= 0) signature = strings[(int) signatureIndex]; ins.Operands.Add(signature); } break; case DebugOpCodes.EndSequence: return; case DebugOpCodes.Special: // between 0x0a and 0xff (inclusive) case DebugOpCodes.SetPrologueEnd: case DebugOpCodes.SetEpilogueBegin: default: break; } } }); }
/// <summary> /// Create debug info for the given (otherwise completed) body. /// </summary> internal void CreateDebugInfo(MethodBody dbody, RegisterMapper regMapper, DexTargetPackage targetPackage) { var source = compiledMethod.ILSource; if ((source == null) || !source.HasBody || (source.Body.Instructions.Count == 0)) return; // Initialize var info = new DebugInfo(dbody); info.Parameters.AddRange(dbody.Owner.Prototype.Parameters.Select(x => x.Name ?? "?")); // Should be at a better location perhaps info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.SetPrologueEnd)); // Get instructions with valid sequence points var validTuples = dbody.Instructions.Select(x => Tuple.Create(x, x.SequencePoint as ISourceLocation)).Where(x => x.Item2 != null && !x.Item2.IsSpecial).ToList(); // Assign line numbers var lineNumbers = AssignLineNumbers(validTuples.Select(x => x.Item2)); // Set default document string lastUrl = null; var setLineStart = true; int lastLine = 0; int lastOffset = 0; var firstDocument = validTuples.Select(x => x.Item2).FirstOrDefault(x => x != null && x.Document != null); if (firstDocument != null) { lastUrl = firstDocument.Document; lastLine = firstDocument.StartLine; info.LineStart = (uint) lastLine; setLineStart = false; // Set on type or when different in debug info. var type = dbody.Owner.Owner; if (string.IsNullOrEmpty(type.SourceFile)) type.SourceFile = lastUrl; if (type.SourceFile != lastUrl) { // Make sure the file is set when needed lastUrl = null; } } // Build intermediate list var entries = new List<Entry>(); // Add line numbers foreach (var tuple in validTuples) { var ins = tuple.Item1; var seqp = tuple.Item2; var lineNumber = GetLineNumber(seqp, lineNumbers); // Line number if (setLineStart) { info.LineStart = (uint)lineNumber; setLineStart = false; } var url = seqp.Document; entries.Add(new PositionEntry(ins.Offset, lineNumber, url)); } // Add variables ControlFlowGraph cfg = null; foreach (var tuple in regMapper.VariableRegisters) { var reg = tuple.Register; var variable = tuple.Variable; if (variable.IsCompilerGenerated) continue; var dexType = variable.GetType(targetPackage); if (dexType == null) continue; // Find out in which basic blocks the variable is live cfg = cfg ?? new ControlFlowGraph(dbody); var startMap = new Dictionary<BasicBlock, Instruction>(); foreach (var block in cfg) { // First instruction from that first writes to the register. var firstWrite = block.Instructions.FirstOrDefault(reg.IsDestinationIn); if (firstWrite == null) continue; // The variable is valid the first instruction after the first write. Instruction start; if (!firstWrite.TryGetNext(dbody.Instructions, out start)) continue; startMap.Add(block, start); block.AddLiveRegisterAtExit(reg); } if (startMap.Count == 0) continue; // Generate start-restart-end entries VariableEndEntry lastBlockEndEntry = null; foreach (var block in cfg) { Instruction start; var started = false; if (block.IsLiveAtEntry(reg)) { // Live in the entire block if (lastBlockEndEntry == null) { // We have to start/restart entries.Add(new VariableStartEntry(block.Entry.Offset, reg, variable, dexType)); } else { // Remove the end-entry of the previous block entries.Remove(lastBlockEndEntry); } started = true; } else if (startMap.TryGetValue(block, out start)) { // Live from "start" entries.Add(new VariableStartEntry(start.Offset, reg, variable, dexType)); started = true; } Instruction next; lastBlockEndEntry = null; if (started && block.Exit.TryGetNext(dbody.Instructions, out next)) { // Add end block entries.Add(lastBlockEndEntry = new VariableEndEntry(next.Offset, reg, variable)); } } // Weave in splilling info var spillMappings = regMapper.RegisterSpillingMap.Find(reg).ToList(); if (spillMappings.Count > 0) { foreach (var mapping in spillMappings) { var alreadyStarted = IsStartedAt(entries, variable, mapping.FirstInstruction.Offset); if (alreadyStarted) { // Stop now var prev = mapping.FirstInstruction.GetPrevious(dbody.Instructions); entries.Add(new VariableEndEntry(prev.Offset, mapping.HighRegister, variable)); } // Add mappings for low register entries.Add(new VariableStartEntry(mapping.FirstInstruction.Offset, mapping.LowRegister, variable, dexType)); entries.Add(new VariableEndEntry(mapping.LastInstruction.Offset, mapping.LowRegister, variable)); if (alreadyStarted) { // Restart on high register Instruction next; if (mapping.LastInstruction.TryGetNext(dbody.Instructions, out next)) { entries.Add(new VariableStartEntry(next.Offset, mapping.HighRegister, variable, dexType)); } } } } } // Generate instructions entries.Sort(); var firstPositionEntry = true; var startedVarirables = new HashSet<Register>(); foreach (var entry in entries) { entry.Generate(info, ref lastLine, ref lastOffset, ref lastUrl, ref firstPositionEntry, startedVarirables); } // Terminate info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.EndSequence)); // Attached dbody.DebugInfo = info; }