/// <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 (!type.SetSourceFile(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; }
/// <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; }