#region Keep in sync with object-internals.h
private int ilOffset = OFFSET_UNKNOWN;
private int nativeOffset = OFFSET_UNKNOWN;
+ private long methodAddress;
private MethodBase methodBase;
private string fileName;
private int lineNumber;
return nativeOffset;
}
+ internal long GetMethodAddress ()
+ {
+ return methodAddress;
+ }
+
internal string GetInternalMethodName ()
{
return internalMethodName;
* of icalls, do not require an increment.
*/
#pragma warning disable 169
- private const int mono_corlib_version = 111;
+ private const int mono_corlib_version = 112;
#pragma warning restore 169
[ComVisible (true)]
if (internal_name != null)
sb.Append (internal_name);
else
- sb.AppendFormat ("<0x{0:x5}> {1}", frame.GetNativeOffset (), unknown);
+ sb.AppendFormat ("<0x{0:x5} + 0x{1:x5}> {2}", frame.GetMethodAddress (), frame.GetNativeOffset (), unknown);
} else {
GetFullNameForStackTrace (sb, frame.GetMethod ());
if (frame.GetILOffset () == -1)
- sb.AppendFormat (" <0x{0:x5}> ", frame.GetNativeOffset ());
+ sb.AppendFormat ("<0x{0:x5} + 0x{1:x5}> ", frame.GetMethodAddress (), frame.GetNativeOffset ());
else
sb.AppendFormat (" [0x{0:x5}] ", frame.GetILOffset ());
--- /dev/null
+using System;
+using System.IO;
+using System.Linq;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Mono.Cecil;
+using Mono.CompilerServices.SymbolWriter;
+
+namespace Symbolicate
+{
+ struct Location {
+ public string FileName;
+ public int Line;
+ }
+
+ class LocationProvider {
+ class AssemblyLocationProvider {
+ AssemblyDefinition assembly;
+ MonoSymbolFile symbolFile;
+
+ public AssemblyLocationProvider (AssemblyDefinition assembly, MonoSymbolFile symbolFile)
+ {
+ this.assembly = assembly;
+ this.symbolFile = symbolFile;
+ }
+
+ public bool TryGetLocation (string methodFullName, string[] methodParamsTypes, int ilOffset, out Location location)
+ {
+ location = default (Location);
+ if (symbolFile == null)
+ return false;
+
+ var typeNameEnd = methodFullName.LastIndexOf (".");
+ var typeName = methodFullName.Substring (0, typeNameEnd);
+ var methodName = methodFullName.Substring (typeNameEnd + 1, methodFullName.Length - typeNameEnd - 1);
+
+ var type = assembly.MainModule.Types.FirstOrDefault (t => t.FullName == typeName);
+ if (type == null)
+ return false;
+
+ var method = type.Methods.FirstOrDefault (m => {
+ if (m.Name != methodName)
+ return false;
+
+ if (m.Parameters.Count != methodParamsTypes.Length)
+ return false;
+
+ for (var i = 0; i < methodParamsTypes.Length; i++) {
+ var paramType = m.Parameters[i].ParameterType;
+ if (paramType.Name != methodParamsTypes[i])
+ return false;
+ }
+
+ return true;
+ });
+
+ if (method == null)
+ return false;
+
+ var methodSymbol = symbolFile.Methods [method.MetadataToken.RID-1];
+
+ foreach (var lineNumber in methodSymbol.GetLineNumberTable ().LineNumbers) {
+ if (lineNumber.Offset < ilOffset)
+ continue;
+
+ location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
+ location.Line = lineNumber.Row;
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ Dictionary<string, AssemblyLocationProvider> assemblies;
+ HashSet<string> directories;
+
+ public LocationProvider () {
+ assemblies = new Dictionary<string, AssemblyLocationProvider> ();
+ directories = new HashSet<string> ();
+ }
+
+ public void AddAssembly (string assemblyPath)
+ {
+ assemblyPath = Path.GetFullPath (assemblyPath);
+ if (assemblies.ContainsKey (assemblyPath))
+ return;
+
+ if (!File.Exists (assemblyPath))
+ throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
+
+ var assembly = AssemblyDefinition.ReadAssembly (assemblyPath);
+ MonoSymbolFile symbolFile = null;
+
+ var symbolPath = assemblyPath + ".mdb";
+ if (!File.Exists (symbolPath))
+ Debug.WriteLine (".mdb file was not found for " + assemblyPath);
+ else
+ symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
+
+ assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile));
+
+ directories.Add (Path.GetDirectoryName (assemblyPath));
+
+ foreach (var assemblyRef in assembly.MainModule.AssemblyReferences) {
+ string refPath = null;
+ foreach (var dir in directories) {
+ refPath = Path.Combine (dir, assemblyRef.Name);
+ if (File.Exists (refPath))
+ break;
+ refPath = Path.Combine (dir, assemblyRef.Name + ".dll");
+ if (File.Exists (refPath))
+ break;
+ refPath = Path.Combine (dir, assemblyRef.Name + ".exe");
+ if (File.Exists (refPath))
+ break;
+ refPath = null;
+ }
+ if (refPath != null)
+ AddAssembly (refPath);
+ }
+ }
+
+ public void AddDirectory (string directory)
+ {
+ if (Directory.Exists (directory))
+ throw new ArgumentException ("Directory " + directory + " does not exist.");
+
+ directories.Add (directory);
+ }
+
+ public bool TryGetLocation (string methodName, string[] methodParams, int ilOffset, out Location location)
+ {
+ location = default (Location);
+ foreach (var assembly in assemblies.Values) {
+ if (assembly.TryGetLocation (methodName, methodParams, ilOffset, out location))
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
+
--- /dev/null
+thisdir = tools/symbolicate
+SUBDIRS =
+include ../../build/rules.make
+
+PROGRAM = symbolicate.exe
+
+LOCAL_MCS_FLAGS = \
+ /r:Mono.Cecil.dll \
+ /r:Mono.CompilerServices.SymbolWriter.dll \
+ /r:System.Xml
+
+include ../../build/executable.make
--- /dev/null
+using System;
+using System.IO;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace Symbolicate
+{
+ public class Program
+ {
+ static Regex regex = new Regex (@"\w*at (?<MethodName>.+) \((?<MethodParams>.*)\) \[0x(?<IL>.+)\] in <filename unknown>:0");
+
+ public static int Main (String[] args)
+ {
+ if (args.Length < 2) {
+ Console.Error.WriteLine ("Usage: symbolicate <assembly path> <input file> [lookup directories]");
+ return 1;
+ }
+
+ var assemblyPath = args [0];
+ var inputFile = args [1];
+
+ var locProvider = new LocationProvider ();
+
+ for (var i = 2; i < args.Length; i++)
+ locProvider.AddDirectory (args [i]);
+
+ locProvider.AddAssembly (assemblyPath);
+
+ using (StreamReader r = new StreamReader (inputFile)) {
+ for (var line = r.ReadLine (); line != null; line = r.ReadLine ()) {
+ line = SymbolicateLine (line, locProvider);
+ Console.WriteLine (line);
+ }
+ }
+
+ return 0;
+ }
+
+ static string SymbolicateLine (string line, LocationProvider locProvider)
+ {
+ var match = regex.Match (line);
+ if (!match.Success)
+ return line;
+
+ var methodName = match.Groups ["MethodName"].Value;
+ var methodParams = ParseParametersTypes (match.Groups ["MethodParams"].Value);
+ var ilOffset = int.Parse (match.Groups ["IL"].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+
+ Location location;
+ if (!locProvider.TryGetLocation (methodName, methodParams, ilOffset, out location))
+ return line;
+
+ return line.Replace ("<filename unknown>:0", string.Format ("{0}:{1}", location.FileName, location.Line));
+ }
+
+ static string[] ParseParametersTypes (string parameters)
+ {
+ if (string.IsNullOrEmpty (parameters))
+ return new string [0];
+
+ var paramsArray = parameters.Split (',');
+ var paramsTypes = new string [paramsArray.Length];
+ for (var i = 0; i < paramsArray.Length; i++)
+ paramsTypes [i] = paramsArray [i].Trim ().Split (new char[]{' '}, 2)[0];
+
+ return paramsTypes;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+symbolicate.cs
+LocationProvider.cs
\ No newline at end of file
* Changes which are already detected at runtime, like the addition
* of icalls, do not require an increment.
*/
-#define MONO_CORLIB_VERSION 111
+#define MONO_CORLIB_VERSION 112
typedef struct
{
MonoObject obj;
gint32 il_offset;
gint32 native_offset;
+ gint64 method_address;
MonoReflectionMethod *method;
MonoString *filename;
gint32 line;
genmdesc_SOURCES = \
mini.h \
+ seq-points.h \
genmdesc.c \
helpers.c \
../metadata/opcodes.c
common_sources = \
mini.c \
+ seq-points.c \
+ seq-points.h \
ir-emit.h \
method-to-ir.c \
decompose.c \
#include <mono/utils/mono-mmap.h>
#include "mini.h"
+#include "seq-points.h"
#include "image-writer.h"
#include "dwarfwriter.h"
#include "mini-gc.h"
{
MonoMethod *method;
int i, k, buf_size, method_index;
- guint32 debug_info_size;
+ guint32 debug_info_size, seq_points_size;
guint8 *code;
MonoMethodHeader *header;
guint8 *p, *buf, *debug_info;
seq_points = cfg->seq_point_info;
- buf_size = header->num_clauses * 256 + debug_info_size + 2048 + (seq_points ? (seq_points->len * 128) : 0) + cfg->gc_map_size;
+ seq_points_size = seq_point_info_get_write_size (seq_points);
+
+ buf_size = header->num_clauses * 256 + debug_info_size + 2048 + seq_points_size + cfg->gc_map_size;
p = buf = g_malloc (buf_size);
use_unwind_ops = cfg->unwind_ops != NULL;
}
}
- if (seq_points) {
- int il_offset, native_offset, last_il_offset, last_native_offset, j;
-
- encode_value (seq_points->len, p, &p);
- last_il_offset = last_native_offset = 0;
- for (i = 0; i < seq_points->len; ++i) {
- SeqPoint *sp = &seq_points->seq_points [i];
- il_offset = sp->il_offset;
- native_offset = sp->native_offset;
- encode_value (il_offset - last_il_offset, p, &p);
- encode_value (native_offset - last_native_offset, p, &p);
- last_il_offset = il_offset;
- last_native_offset = native_offset;
+ if (seq_points)
+ p += seq_point_info_write (seq_points, p);
- encode_value (sp->flags, p, &p);
- encode_value (sp->next_len, p, &p);
- for (j = 0; j < sp->next_len; ++j)
- encode_value (sp->next [j], p, &p);
- }
- }
-
g_assert (debug_info_size < buf_size);
encode_value (debug_info_size, p, &p);
MonoDebugOptions *opt = mini_get_debug_options ();
opt->mdb_optimizations = TRUE;
- opt->gen_seq_points = TRUE;
+ opt->gen_seq_points_debug_data = TRUE;
if (!mono_debug_enabled ()) {
aot_printerrf (acfg, "The soft-debug AOT option requires the --debug option.\n");
#include <mono/utils/mono-digest.h>
#include "mini.h"
+#include "seq-points.h"
#include "version.h"
#ifndef DISABLE_AOT
if (method && has_seq_points) {
MonoSeqPointInfo *seq_points;
- int il_offset, native_offset, last_il_offset, last_native_offset, j;
- int len = decode_value (p, &p);
-
- seq_points = g_malloc0 (sizeof (MonoSeqPointInfo) + (len - MONO_ZERO_LEN_ARRAY) * sizeof (SeqPoint));
- seq_points->len = len;
- last_il_offset = last_native_offset = 0;
- for (i = 0; i < len; ++i) {
- SeqPoint *sp = &seq_points->seq_points [i];
- il_offset = last_il_offset + decode_value (p, &p);
- native_offset = last_native_offset + decode_value (p, &p);
-
- sp->il_offset = il_offset;
- sp->native_offset = native_offset;
-
- sp->flags = decode_value (p, &p);
- sp->next_len = decode_value (p, &p);
- sp->next = g_new (int, sp->next_len);
- for (j = 0; j < sp->next_len; ++j)
- sp->next [j] = decode_value (p, &p);
-
- last_il_offset = il_offset;
- last_native_offset = native_offset;
- }
+ p += seq_point_info_read (&seq_points, p, FALSE);
mono_domain_lock (domain);
g_hash_table_insert (domain_jit_info (domain)->seq_points, method, seq_points);
br: len:6
label: len:0
seq_point: len:31 clob:c
+il_seq_point: len:0
long_add: dest:i src1:i src2:i len:3 clob:1 nacl:6
long_sub: dest:i src1:i src2:i len:3 clob:1 nacl:6
# See the comment in resume_from_signal_handler, we can't copy the fp regs from sigctx to MonoContext on linux,
# since the corresponding sigctx structures are not well defined.
seq_point: len:38 clob:c
+il_seq_point: len:0
throw: src1:i len:24
rethrow: src1:i len:20
# See the comment in resume_from_signal_handler, we can't copy the fp regs from sigctx to MonoContext on linux,
# since the corresponding sigctx structures are not well defined.
seq_point: len:38 clob:c
+il_seq_point: len:0
throw: src1:i len:24
rethrow: src1:i len:20
br: len:16
switch: src1:i len:40
seq_point: len:24
+il_seq_point: len:0
int_conv_to_r_un: dest:f src1:i len:32
throw: src1:i len:24
relaxed_nop: len:4
break: len:32
seq_point: len:24
+il_seq_point: len:0
jmp: len:108
tailcall: len:120 clob:c
call: dest:a clob:c len:16
relaxed_nop: len:4
break: len:40
seq_point: len:48
+il_seq_point: len:0
jmp: len:96
call: dest:a clob:c len:36
br: len:4
sbb: dest:i src1:i src2:i len:6
sbb_imm: dest:i src1:i len:14
seq_point: len:54
+il_seq_point: len:0
sext_i4: dest:i src1:i len:4
zext_i4: dest:i src1:i len:4
shl_imm: dest:i src1:i len:10
tailcall: len:120 clob:c
br: len:5
seq_point: len:17
+il_seq_point: len:0
int_beq: len:6
int_bge: len:6
#include <mono/utils/mono-threads.h>
#include "debugger-agent.h"
#include "mini.h"
+#include "seq-points.h"
/*
On iOS we can't use System.Environment.Exit () as it will do the wrong
breakpoints_init ();
suspend_init ();
- mini_get_debug_options ()->gen_seq_points = TRUE;
+ mini_get_debug_options ()->gen_seq_points_debug_data = TRUE;
/*
* This is needed because currently we don't handle liveness info.
*/
return count_threads_to_wait_for () == 0;
}
-static MonoSeqPointInfo*
-get_seq_points (MonoDomain *domain, MonoMethod *method)
-{
- MonoSeqPointInfo *seq_points;
-
- mono_domain_lock (domain);
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
- if (!seq_points && method->is_inflated) {
- /* generic sharing + aot */
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (method));
- if (!seq_points)
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mini_get_shared_method (method));
- }
- mono_domain_unlock (domain);
-
- return seq_points;
-}
-
static void
no_seq_points_found (MonoMethod *method)
{
printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE));
}
-/*
- * find_next_seq_point_for_native_offset:
- *
- * Find the first sequence point after NATIVE_OFFSET.
- */
-static SeqPoint*
-find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
-{
- MonoSeqPointInfo *seq_points;
- int i;
-
- seq_points = get_seq_points (domain, method);
- if (!seq_points) {
- if (info)
- *info = NULL;
- return NULL;
- }
- g_assert (seq_points);
- if (info)
- *info = seq_points;
-
- for (i = 0; i < seq_points->len; ++i) {
- if (seq_points->seq_points [i].native_offset >= native_offset)
- return &seq_points->seq_points [i];
- }
-
- return NULL;
-}
-
-/*
- * find_prev_seq_point_for_native_offset:
- *
- * Find the first sequence point before NATIVE_OFFSET.
- */
-static SeqPoint*
-find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
-{
- MonoSeqPointInfo *seq_points;
- int i;
-
- seq_points = get_seq_points (domain, method);
- if (info)
- *info = seq_points;
- if (!seq_points)
- return NULL;
-
- for (i = seq_points->len - 1; i >= 0; --i) {
- if (seq_points->seq_points [i].native_offset <= native_offset)
- return &seq_points->seq_points [i];
- }
-
- return NULL;
-}
-
-/*
- * find_seq_point:
- *
- * Find the sequence point corresponding to the IL offset IL_OFFSET, which
- * should be the location of a sequence point.
- */
-static G_GNUC_UNUSED SeqPoint*
-find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info)
-{
- MonoSeqPointInfo *seq_points;
- int i;
-
- *info = NULL;
-
- seq_points = get_seq_points (domain, method);
- if (!seq_points)
- return NULL;
- *info = seq_points;
-
- for (i = 0; i < seq_points->len; ++i) {
- if (seq_points->seq_points [i].il_offset == il_offset)
- return &seq_points->seq_points [i];
- }
-
- return NULL;
-}
-
typedef struct {
DebuggerTlsData *tls;
GSList *frames;
ComputeFramesUserData *ud = user_data;
StackFrame *frame;
MonoMethod *method, *actual_method, *api_method;
- SeqPoint *sp;
+ SeqPoint sp;
int flags = 0;
if (info->type != FRAME_TYPE_MANAGED) {
if (info->il_offset == -1) {
/* mono_debug_il_offset_from_address () doesn't seem to be precise enough (#2092) */
if (ud->frames == NULL) {
- sp = find_prev_seq_point_for_native_offset (info->domain, method, info->native_offset, NULL);
- if (sp)
- info->il_offset = sp->il_offset;
+ if (find_prev_seq_point_for_native_offset (info->domain, method, info->native_offset, NULL, &sp))
+ info->il_offset = sp.il_offset;
}
if (info->il_offset == -1)
info->il_offset = mono_debug_il_offset_from_address (method, info->domain, info->native_offset);
guint8 *ip;
MonoJitInfo *ji;
MonoDomain *domain;
- SeqPoint *sp;
} BreakpointInstance;
/*
static void
insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp, MonoError *error)
{
- int i, count;
+ int count;
BreakpointInstance *inst;
- SeqPoint *sp = NULL;
+ SeqPointIterator it;
+ gboolean it_has_sp = FALSE;
if (error)
mono_error_init (error);
- for (i = 0; i < seq_points->len; ++i) {
- sp = &seq_points->seq_points [i];
-
- if (sp->il_offset == bp->il_offset)
+ seq_point_iterator_init (&it, seq_points);
+ while (seq_point_iterator_next (&it)) {
+ if (it.seq_point.il_offset == bp->il_offset) {
+ it_has_sp = TRUE;
break;
+ }
}
- if (i == seq_points->len) {
+ if (!it_has_sp) {
/*
* The set of IL offsets with seq points doesn't completely match the
* info returned by CMD_METHOD_GET_DEBUG_INFO (#407).
*/
- for (i = 0; i < seq_points->len; ++i) {
- sp = &seq_points->seq_points [i];
-
- if (sp->il_offset != METHOD_ENTRY_IL_OFFSET && sp->il_offset != METHOD_EXIT_IL_OFFSET && sp->il_offset + 1 == bp->il_offset)
+ seq_point_iterator_init (&it, seq_points);
+ while (seq_point_iterator_next (&it)) {
+ if (it.seq_point.il_offset != METHOD_ENTRY_IL_OFFSET &&
+ it.seq_point.il_offset != METHOD_EXIT_IL_OFFSET &&
+ it.seq_point.il_offset + 1 == bp->il_offset) {
+ it_has_sp = TRUE;
break;
+ }
}
}
- if (i == seq_points->len) {
- char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset, seq_points->len);
+ if (!it_has_sp) {
+ char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset);
- for (i = 0; i < seq_points->len; ++i)
- DEBUG (1, fprintf (log_file, "%d\n", seq_points->seq_points [i].il_offset));
+ seq_point_iterator_init (&it, seq_points);
+ while (seq_point_iterator_next (&it))
+ DEBUG (1, fprintf (log_file, "%d\n", it.seq_point.il_offset));
if (error) {
mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
}
inst = g_new0 (BreakpointInstance, 1);
- inst->sp = sp;
- inst->native_offset = sp->native_offset;
- inst->ip = (guint8*)ji->code_start + sp->native_offset;
+ inst->il_offset = it.seq_point.il_offset;
+ inst->native_offset = it.seq_point.native_offset;
+ inst->ip = (guint8*)ji->code_start + it.seq_point.native_offset;
inst->ji = ji;
inst->domain = domain;
g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
dbg_unlock ();
- if (sp->native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
+ if (it.seq_point.native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
DEBUG (1, fprintf (log_file, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset));
} else if (count == 0) {
#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
#endif
}
- DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x [%p](%d).\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)sp->il_offset, inst->ip, count));
+ DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x [%p](%d).\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)it.seq_point.il_offset, inst->ip, count));
}
static void
MonoContext *ctx = &tls->restore_ctx;
MonoMethod *method;
MonoSeqPointInfo *info;
- SeqPoint *sp;
+ SeqPoint sp;
+ gboolean found_sp;
// FIXME: Speed this up
* The ip points to the instruction causing the breakpoint event, which is after
* the offset recorded in the seq point map, so find the prev seq point before ip.
*/
- sp = find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info);
- if (!sp)
+ found_sp = find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
+
+ if (!found_sp)
no_seq_points_found (method);
- g_assert (sp);
- DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), method->name, ip, native_offset, sp ? sp->il_offset : -1));
+ g_assert (found_sp);
+
+ DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), method->name, ip, native_offset, sp.il_offset));
bp = NULL;
for (i = 0; i < breakpoints->len; ++i) {
for (j = 0; j < bp->children->len; ++j) {
inst = g_ptr_array_index (bp->children, j);
- if (inst->ji == ji && inst->sp == sp) {
+ if (inst->ji == ji && inst->il_offset == sp.il_offset && inst->native_offset == sp.native_offset) {
if (bp->req->event_kind == EVENT_KIND_STEP) {
g_ptr_array_add (ss_reqs_orig, bp->req);
} else {
}
if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
/* Maybe a method entry/exit event */
- if (sp->il_offset == METHOD_ENTRY_IL_OFFSET)
+ if (sp.il_offset == METHOD_ENTRY_IL_OFFSET)
kind = EVENT_KIND_METHOD_ENTRY;
- else if (sp->il_offset == METHOD_EXIT_IL_OFFSET)
+ else if (sp.il_offset == METHOD_EXIT_IL_OFFSET)
kind = EVENT_KIND_METHOD_EXIT;
}
if (mono_thread_internal_current () != ss_req->thread)
continue;
- hit = ss_update (ss_req, ji, sp, tls, ctx);
+ hit = ss_update (ss_req, ji, &sp, tls, ctx);
if (hit)
g_ptr_array_add (ss_reqs, req);
/* Start single stepping again from the current sequence point */
- ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
+ ss_start (ss_req, method, &sp, info, ctx, tls, FALSE);
}
if (ss_reqs->len > 0)
GSList *events;
MonoContext *ctx = &tls->restore_ctx;
MonoMethod *method;
- SeqPoint *sp;
+ SeqPoint sp;
MonoSeqPointInfo *info;
ip = MONO_CONTEXT_GET_IP (ctx);
* The ip points to the instruction causing the single step event, which is before
* the offset recorded in the seq point map, so find the next seq point after ip.
*/
- sp = find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info);
- if (!sp)
+ if (!find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp))
return;
- il_offset = sp->il_offset;
- if (!ss_update (ss_req, ji, sp, tls, ctx))
+ il_offset = sp.il_offset;
+
+ if (!ss_update (ss_req, ji, &sp, tls, ctx))
return;
/* Start single stepping again from the current sequence point */
- ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
+ ss_start (ss_req, method, &sp, info, ctx, tls, FALSE);
if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) &&
(method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
* belong to the same thread as CTX.
*/
static void
-ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch)
+ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch)
{
int i, j, frame_index;
SeqPoint *next_sp;
+ SeqPoint local_sp;
+ gboolean found_sp;
MonoBreakpoint *bp;
gboolean enable_global = FALSE;
StackFrame *frame = tls->frames [frame_index];
method = frame->method;
- sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+ found_sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info, &local_sp);
+ sp = (found_sp)? &local_sp : NULL;
frame_index ++;
if (sp && sp->next_len != 0)
break;
StackFrame *frame = tls->frames [frame_index];
method = frame->method;
- sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+ found_sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info, &local_sp);
+ sp = (found_sp)? &local_sp : NULL;
if (sp && sp->next_len != 0)
break;
sp = NULL;
}
if (sp && sp->next_len > 0) {
- for (i = 0; i < sp->next_len; ++i) {
- next_sp = &info->seq_points [sp->next [i]];
+ SeqPoint* next = g_new(SeqPoint, sp->next_len);
+
+ seq_point_init_next (info, *sp, next);
+ for (i = 0; i < sp->next_len; i++) {
+ next_sp = &next[i];
bp = set_breakpoint (method, next_sp->il_offset, ss_req->req, NULL);
ss_req->bps = g_slist_append (ss_req->bps, bp);
}
+ g_free (next);
}
if (ss_req->depth == STEP_DEPTH_OVER) {
for (j = 0; j < jinfo->num_clauses; ++j) {
MonoJitExceptionInfo *ei = &jinfo->clauses [j];
- sp = find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL);
+ found_sp = find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp);
+ sp = (found_sp)? &local_sp : NULL;
if (sp) {
bp = set_breakpoint (frame->method, sp->il_offset, ss_req->req, NULL);
ss_req->bps = g_slist_append (ss_req->bps, bp);
DebuggerTlsData *tls;
MonoSeqPointInfo *info = NULL;
SeqPoint *sp = NULL;
+ SeqPoint local_sp;
+ gboolean found_sp;
MonoMethod *method = NULL;
MonoDebugMethodInfo *minfo;
gboolean step_to_catch = FALSE;
* Find the seq point corresponding to the landing site ip, which is the first seq
* point after ip.
*/
- sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info);
+ found_sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info, &local_sp);
+ sp = (found_sp)? &local_sp : NULL;
if (!sp)
no_seq_points_found (frame.method);
g_assert (sp);
if (!method && frame->il_offset != -1) {
/* FIXME: Sort the table and use a binary search */
- sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+ found_sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info, &local_sp);
+ sp = (found_sp)? &local_sp : NULL;
if (!sp)
no_seq_points_found (frame->method);
g_assert (sp);
MonoMethod *method;
MonoDomain *domain;
MonoSeqPointInfo *seq_points;
- SeqPoint *sp = NULL;
+ SeqPoint sp;
+ gboolean found_sp;
gint64 il_offset;
- int i;
method = decode_methodid (p, &p, end, &domain, &err);
if (err)
if (tls->frame_count == 0 || tls->frames [0]->actual_method != method)
return ERR_INVALID_ARGUMENT;
- seq_points = get_seq_points (domain, method);
- g_assert (seq_points);
+ found_sp = find_seq_point (domain, method, il_offset, &seq_points, &sp);
- for (i = 0; i < seq_points->len; ++i) {
- sp = &seq_points->seq_points [i];
+ g_assert (seq_points);
- if (sp->il_offset == il_offset)
- break;
- }
- if (i == seq_points->len)
+ if (!found_sp)
return ERR_INVALID_ARGUMENT;
// FIXME: Check that the ip change is safe
- DEBUG (1, fprintf (log_file, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp->il_offset, (int)sp->native_offset));
- MONO_CONTEXT_SET_IP (&tls->restore_ctx, (guint8*)tls->frames [0]->ji->code_start + sp->native_offset);
+ DEBUG (1, fprintf (log_file, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp.il_offset, (int)sp.native_offset));
+ MONO_CONTEXT_SET_IP (&tls->restore_ctx, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset);
break;
}
default:
}
#endif
-
#include "mini.h"
-/* IL offsets used to mark the sequence points belonging to method entry/exit events */
-#define METHOD_ENTRY_IL_OFFSET -1
-#define METHOD_EXIT_IL_OFFSET 0xffffff
-
-/* Native offset used to mark seq points in dead code */
-#define SEQ_POINT_NATIVE_OFFSET_DEAD_CODE -1
-
void
mono_debugger_agent_parse_options (char *options) MONO_INTERNAL;
} while (0)
#define NEW_SEQ_POINT(cfg,dest,il_offset,intr_loc) do { \
- MONO_INST_NEW ((cfg), (dest), OP_SEQ_POINT); \
+ MONO_INST_NEW ((cfg), (dest), cfg->gen_seq_points_debug_data ? OP_SEQ_POINT : OP_IL_SEQ_POINT); \
(dest)->inst_imm = (il_offset); \
(dest)->flags = intr_loc ? MONO_INST_SINGLE_STEP_LOC : 0; \
} while (0)
#include "jit-icalls.h"
#include "jit.h"
#include "debugger-agent.h"
+#include "seq-points.h"
#define BRANCH_COST 10
#define INLINE_LENGTH_LIMIT 20
seq_points = FALSE;
}
- if (cfg->gen_seq_points && cfg->method == method) {
+ if (cfg->gen_seq_points_debug_data && cfg->method == method) {
minfo = mono_debug_lookup_method (method);
if (minfo) {
int i, n_il_offsets;
case OP_NOT_REACHED:
case OP_NOT_NULL:
break;
+ case OP_IL_SEQ_POINT:
+ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+ break;
case OP_SEQ_POINT: {
int i;
case OP_NOT_REACHED:
case OP_NOT_NULL:
break;
+ case OP_IL_SEQ_POINT:
+ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+ break;
case OP_SEQ_POINT: {
int i;
MonoInst *info_var = cfg->arch.seq_point_info_var;
case OP_GC_LIVENESS_USE:
printf (" R%d", (int)ins->inst_c1);
break;
+ case OP_IL_SEQ_POINT:
case OP_SEQ_POINT:
printf (" il: %x", (int)ins->inst_imm);
break;
#include "mini.h"
#include "trace.h"
#include "debugger-agent.h"
+#include "seq-points.h"
#ifndef MONO_ARCH_CONTEXT_DEF
#define MONO_ARCH_CONTEXT_DEF
}
else
MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, method, NULL));
+ sf->method_address = (gint64) ji->code_start;
sf->native_offset = (char *)ip - (char *)ji->code_start;
/*
* operation, so we shouldn't call this method twice.
*/
location = mono_debug_lookup_source_location (jinfo_get_method (ji), sf->native_offset, domain);
- if (location)
+ if (location) {
sf->il_offset = location->il_offset;
- else
- sf->il_offset = 0;
+ } else {
+ SeqPoint sp;
+ if (find_prev_seq_point_for_native_offset (domain, jinfo_get_method (ji), sf->native_offset, NULL, &sp))
+ sf->il_offset = sp.il_offset;
+ else
+ sf->il_offset = 0;
+ }
if (need_file_info) {
if (location && location->source_file) {
MonoDebugSourceLocation *source;
source = mono_debug_lookup_source_location (jinfo_get_method (frame.ji), frame.native_offset, domain);
- il_offset = source ? source->il_offset : -1;
+ if (source) {
+ il_offset = source->il_offset;
+ } else {
+ SeqPoint sp;
+ if (find_prev_seq_point_for_native_offset (domain, jinfo_get_method (frame.ji), frame.native_offset, NULL, &sp))
+ il_offset = sp.il_offset;
+ }
mono_debug_free_source_location (source);
} else
il_offset = -1;
case OP_NOT_REACHED:
case OP_NOT_NULL:
break;
+ case OP_IL_SEQ_POINT:
+ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+ break;
case OP_SEQ_POINT: {
if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
guint32 addr = (guint32)ss_trigger_page;
MINI_OP(OP_LOCALLOC_IMM, "localloc_imm", IREG, NONE, NONE)
MINI_OP(OP_CHECK_THIS, "checkthis", NONE, IREG, NONE)
MINI_OP(OP_SEQ_POINT, "seq_point", NONE, NONE, NONE)
+MINI_OP(OP_IL_SEQ_POINT, "il_seq_point", NONE, NONE, NONE)
MINI_OP(OP_IMPLICIT_EXCEPTION, "implicit_exception", NONE, NONE, NONE)
MINI_OP(OP_VOIDCALL, "voidcall", NONE, NONE, NONE)
case OP_NOT_REACHED:
case OP_NOT_NULL:
break;
+ case OP_IL_SEQ_POINT:
+ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+ break;
case OP_SEQ_POINT: {
int i;
case OP_NOT_NULL: {
}
break;
+ case OP_IL_SEQ_POINT:
+ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+ break;
case OP_SEQ_POINT: {
int i;
case OP_NOT_REACHED:
case OP_NOT_NULL:
break;
+ case OP_IL_SEQ_POINT:
+ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+ break;
case OP_SEQ_POINT: {
int i;
#include <mono/utils/mono-threads.h>
#include "mini.h"
+#include "seq-points.h"
#include "mini-llvm.h"
#include "tasklets.h"
#include <string.h>
#include "mini-gc.h"
#include "debugger-agent.h"
+#include "seq-points.h"
static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException **ex);
}
}
-static void
-collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
-{
- int i;
- MonoBasicBlock *in_bb;
- GSList *l;
-
- for (i = 0; i < bb->in_count; ++i) {
- in_bb = bb->in_bb [i];
-
- if (in_bb->last_seq_point) {
- int src_index = in_bb->last_seq_point->backend.size;
- int dst_index = ins->backend.size;
-
- /* bb->in_bb might contain duplicates */
- for (l = next [src_index]; l; l = l->next)
- if (GPOINTER_TO_UINT (l->data) == dst_index)
- break;
- if (!l)
- next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
- } else {
- /* Have to look at its predecessors */
- if (depth < 5)
- collect_pred_seq_points (in_bb, ins, next, depth + 1);
- }
- }
-}
-
-static void
-mono_save_seq_point_info (MonoCompile *cfg)
-{
- MonoBasicBlock *bb;
- GSList *bb_seq_points, *l;
- MonoInst *last;
- MonoDomain *domain = cfg->domain;
- int i;
- MonoSeqPointInfo *info;
- GSList **next;
-
- if (!cfg->seq_points)
- return;
-
- info = g_malloc0 (sizeof (MonoSeqPointInfo) + (cfg->seq_points->len * sizeof (SeqPoint)));
- info->len = cfg->seq_points->len;
- for (i = 0; i < cfg->seq_points->len; ++i) {
- SeqPoint *sp = &info->seq_points [i];
- MonoInst *ins = g_ptr_array_index (cfg->seq_points, i);
-
- sp->il_offset = ins->inst_imm;
- sp->native_offset = ins->inst_offset;
- if (ins->flags & MONO_INST_NONEMPTY_STACK)
- sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
-
- /* Used below */
- ins->backend.size = i;
- }
-
- /*
- * For each sequence point, compute the list of sequence points immediately
- * following it, this is needed to implement 'step over' in the debugger agent.
- */
- next = g_new0 (GSList*, cfg->seq_points->len);
- for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- bb_seq_points = g_slist_reverse (bb->seq_points);
- last = NULL;
- for (l = bb_seq_points; l; l = l->next) {
- MonoInst *ins = l->data;
-
- if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
- /* Used to implement method entry/exit events */
- continue;
- if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
- continue;
-
- if (last != NULL) {
- /* Link with the previous seq point in the same bb */
- next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
- } else {
- /* Link with the last bb in the previous bblocks */
- collect_pred_seq_points (bb, ins, next, 0);
- }
-
- last = ins;
- }
-
- if (bb->last_ins && bb->last_ins->opcode == OP_ENDFINALLY && bb->seq_points) {
- MonoBasicBlock *bb2;
- MonoInst *endfinally_seq_point = NULL;
-
- /*
- * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
- */
- l = g_slist_last (bb->seq_points);
- if (l) {
- endfinally_seq_point = l->data;
-
- for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
- GSList *l = g_slist_last (bb2->seq_points);
-
- if (l) {
- MonoInst *ins = l->data;
-
- if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
- next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
- }
- }
- }
- }
- }
-
- if (cfg->verbose_level > 2) {
- printf ("\nSEQ POINT MAP: \n");
- }
-
- for (i = 0; i < cfg->seq_points->len; ++i) {
- SeqPoint *sp = &info->seq_points [i];
- GSList *l;
- int j, next_index;
-
- sp->next_len = g_slist_length (next [i]);
- sp->next = g_new (int, sp->next_len);
- j = 0;
- if (cfg->verbose_level > 2 && next [i]) {
- printf ("\tIL0x%x ->", sp->il_offset);
- for (l = next [i]; l; l = l->next) {
- next_index = GPOINTER_TO_UINT (l->data);
- printf (" IL0x%x", info->seq_points [next_index].il_offset);
- }
- printf ("\n");
- }
- for (l = next [i]; l; l = l->next) {
- next_index = GPOINTER_TO_UINT (l->data);
- sp->next [j ++] = next_index;
- }
- g_slist_free (next [i]);
- }
- g_free (next);
-
- cfg->seq_point_info = info;
-
- // FIXME: dynamic methods
- if (!cfg->compile_aot) {
- mono_domain_lock (domain);
- // FIXME: How can the lookup succeed ?
- if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
- g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, info);
- mono_domain_unlock (domain);
- }
-
- g_ptr_array_free (cfg->seq_points, TRUE);
- cfg->seq_points = NULL;
-}
-
void
mono_codegen (MonoCompile *cfg)
{
cfg->full_aot = full_aot;
cfg->skip_visibility = method->skip_visibility;
cfg->orig_method = method;
- cfg->gen_seq_points = debug_options.gen_seq_points;
+ cfg->gen_seq_points = TRUE;
+ cfg->gen_seq_points_debug_data = debug_options.gen_seq_points_debug_data;
+
cfg->explicit_null_checks = debug_options.explicit_null_checks;
cfg->soft_breakpoints = debug_options.soft_breakpoints;
cfg->check_pinvoke_callconv = debug_options.check_pinvoke_callconv;
else if (!strcmp (arg, "explicit-null-checks"))
debug_options.explicit_null_checks = TRUE;
else if (!strcmp (arg, "gen-seq-points"))
- debug_options.gen_seq_points = TRUE;
+ debug_options.gen_seq_points_debug_data = TRUE;
else if (!strcmp (arg, "init-stacks"))
debug_options.init_stacks = TRUE;
else if (!strcmp (arg, "casts"))
mono_counters_register ("Allocated vars", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.allocate_var);
mono_counters_register ("Code reallocs", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.code_reallocs);
mono_counters_register ("Allocated code size", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.allocated_code_size);
+ mono_counters_register ("Allocated seq points size", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.allocated_seq_points_size);
mono_counters_register ("Inlineable methods", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.inlineable_methods);
mono_counters_register ("Inlined methods", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.inlined_methods);
mono_counters_register ("Regvars", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.regvars);
}
static void runtime_invoke_info_free (gpointer value);
-static void seq_point_info_free (gpointer value);
static gint
class_method_pair_equal (gconstpointer ka, gconstpointer kb)
g_free (info);
}
-static void seq_point_info_free (gpointer value)
-{
- int i = 0;
- MonoSeqPointInfo* info = (MonoSeqPointInfo*)value;
-
- for (i = 0; i < info->len; ++i) {
- SeqPoint *sp = &info->seq_points [i];
- g_free (sp->next);
- }
-
- g_free (info);
-}
-
static void
mini_free_jit_domain_info (MonoDomain *domain)
{
*/
typedef MonoStackFrameInfo StackFrameInfo;
-#define MONO_SEQ_POINT_FLAG_NONEMPTY_STACK 1
-
-typedef struct {
- int il_offset, native_offset, flags;
- /* Indexes of successor sequence points */
- int *next;
- /* Number of entries in next */
- int next_len;
-} SeqPoint;
-
-typedef struct {
- int len;
- SeqPoint seq_points [MONO_ZERO_LEN_ARRAY];
-} MonoSeqPointInfo;
-
#if 0
#define mono_bitset_foreach_bit(set,b,n) \
for (b = 0; b < n; b++)\
guint uses_simd_intrinsics : 1;
guint keep_cil_nops : 1;
guint gen_seq_points : 1;
+ guint gen_seq_points_debug_data : 1;
guint explicit_null_checks : 1;
guint compute_gc_maps : 1;
guint soft_breakpoints : 1;
GPtrArray *seq_points;
/* The encoded sequence point info */
- MonoSeqPointInfo *seq_point_info;
+ struct MonoSeqPointInfo *seq_point_info;
/* Method headers which need to be freed after compilation */
GSList *headers_to_free;
gint32 max_code_size_ratio;
gint32 biggest_method_size;
gint32 allocated_code_size;
+ gint32 allocated_seq_points_size;
gint32 inlineable_methods;
gint32 inlined_methods;
gint32 basic_blocks;
gboolean suspend_on_unhandled;
gboolean dyn_runtime_invoke;
gboolean gdb;
- gboolean gen_seq_points;
+ /*
+ * Whenever data such as next sequence points and flags is required.
+ * Next sequence points and flags are required by the debugger agent.
+ */
+ gboolean gen_seq_points_debug_data;
gboolean explicit_null_checks;
/*
* Fill stack frames with 0x2a in method prologs. This helps with the
--- /dev/null
+/*
+ * seq-points.c: Sequence Points functions
+ *
+ * Authors:
+ * Marcos Henrich (marcos.henrich@xamarin.com)
+ *
+ * Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
+ */
+
+#include "mini.h"
+#include "seq-points.h"
+
+typedef struct {
+ guint8 *data;
+ int len;
+ /* When has_debug_data is set to false only il and native deltas are saved */
+ gboolean has_debug_data;
+ /* When alloc_data is set to true data allocation/deallocation is managed by this structure */
+ gboolean alloc_data;
+} SeqPointInfoInflated;
+
+static int
+encode_var_int (guint8 *buf, guint8 **out_buf, int val)
+{
+ guint8 size = 0;
+
+ do {
+ guint8 byte = val & 0x7f;
+ g_assert (size < 4 && "value has more than 28 bits");
+ val >>= 7;
+ if(val) byte |= 0x80;
+ *(buf++) = byte;
+ size++;
+ } while (val);
+
+ if (out_buf)
+ *out_buf = buf;
+
+ return size;
+}
+
+static int
+decode_var_int (guint8* buf, guint8 **out_buf)
+{
+ guint8* p = buf;
+
+ int low;
+ int b;
+ b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
+
+ g_assert (FALSE && "value has more than 28 bits");
+
+done:
+
+ if (out_buf)
+ *out_buf = p;
+
+ return low;
+}
+
+static guint32
+encode_zig_zag (int val)
+{
+ return (val << 1) ^ (val >> 31);
+}
+
+static int
+decode_zig_zag (guint32 val)
+{
+ int n = val;
+ return (n >> 1) ^ (-(n & 1));
+}
+
+static SeqPointInfoInflated
+seq_point_info_inflate (MonoSeqPointInfo *info)
+{
+ SeqPointInfoInflated info_inflated;
+ guint8 *ptr = (guint8*) info;
+ int value;
+
+ value = decode_var_int (ptr, &ptr);
+
+ info_inflated.len = value >> 2;
+ info_inflated.has_debug_data = (value & 1) != 0;
+ info_inflated.alloc_data = (value & 2) != 0;
+
+ if (info_inflated.alloc_data)
+ info_inflated.data = ptr;
+ else
+ memcpy (&info_inflated.data, ptr, sizeof (guint8*));
+
+ return info_inflated;
+}
+
+static MonoSeqPointInfo*
+seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data)
+{
+ MonoSeqPointInfo *info;
+ guint8 *info_ptr;
+ guint8 buffer[4];
+ int buffer_len;
+ int value;
+ int data_size;
+
+ value = len << 2;
+ if (has_debug_data)
+ value |= 1;
+ if (alloc_data)
+ value |= 2;
+
+ buffer_len = encode_var_int (buffer, NULL, value);
+
+ data_size = buffer_len + (alloc_data? len : sizeof (guint8*));
+ info_ptr = g_new0 (guint8, data_size);
+ info = (MonoSeqPointInfo*) info_ptr;
+
+ memcpy (info_ptr, buffer, buffer_len);
+ info_ptr += buffer_len;
+
+ if (alloc_data)
+ memcpy (info_ptr, data, len);
+ else
+ memcpy (info_ptr, &data, sizeof (guint8*));
+
+ mono_jit_stats.allocated_seq_points_size += data_size;
+
+ return info;
+}
+
+void
+seq_point_info_free (gpointer ptr)
+{
+ MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
+ g_free (info);
+}
+
+static int
+seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
+{
+ int value;
+ guint8* ptr0 = ptr;
+
+ value = decode_var_int (ptr, &ptr);
+ seq_point->il_offset += decode_zig_zag (value);
+
+ value = decode_var_int (ptr, &ptr);
+ seq_point->native_offset += decode_zig_zag (value);
+
+ if (has_debug_data) {
+ value = decode_var_int (ptr, &ptr);
+ seq_point->flags = value;
+
+ if (seq_point->flags & MONO_SEQ_POINT_FLAG_EXIT_IL)
+ seq_point->il_offset = METHOD_EXIT_IL_OFFSET;
+
+ value = decode_var_int (ptr, &ptr);
+ seq_point->next_len = value;
+
+ if (seq_point->next_len) {
+ // store next offset and skip it
+ seq_point->next_offset = ptr - buffer_ptr;
+ ptr += seq_point->next_len;
+ }
+ }
+
+ return ptr - ptr0;
+}
+
+static gboolean
+seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data)
+{
+ int il_delta, native_delta;
+ GSList *l;
+ guint8 buffer[4];
+ guint8 len;
+ int flags;
+
+ if (!has_debug_data &&
+ (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
+ return FALSE;
+
+ il_delta = sp->il_offset - last_seq_point->il_offset;
+ native_delta = sp->native_offset - last_seq_point->native_offset;
+
+ flags = sp->flags;
+
+ if (has_debug_data && sp->il_offset == METHOD_EXIT_IL_OFFSET) {
+ il_delta = 0;
+ flags |= MONO_SEQ_POINT_FLAG_EXIT_IL;
+ }
+
+ len = encode_var_int (buffer, NULL, encode_zig_zag (il_delta));
+ g_byte_array_append (array, buffer, len);
+
+ len = encode_var_int (buffer, NULL, encode_zig_zag (native_delta));
+ g_byte_array_append (array, buffer, len);
+
+ if (has_debug_data) {
+ sp->next_offset = array->len;
+ sp->next_len = g_slist_length (next);
+
+ len = encode_var_int (buffer, NULL, flags);
+ g_byte_array_append (array, buffer, len);
+
+ len = encode_var_int (buffer, NULL, sp->next_len);
+ g_byte_array_append (array, buffer, len);
+
+ for (l = next; l; l = l->next) {
+ int next_index = GPOINTER_TO_UINT (l->data);
+ guint8 buffer[4];
+ int len = encode_var_int (buffer, NULL, next_index);
+ g_byte_array_append (array, buffer, len);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
+{
+ int i;
+ MonoBasicBlock *in_bb;
+ GSList *l;
+
+ for (i = 0; i < bb->in_count; ++i) {
+ in_bb = bb->in_bb [i];
+
+ if (in_bb->last_seq_point) {
+ int src_index = in_bb->last_seq_point->backend.size;
+ int dst_index = ins->backend.size;
+
+ /* bb->in_bb might contain duplicates */
+ for (l = next [src_index]; l; l = l->next)
+ if (GPOINTER_TO_UINT (l->data) == dst_index)
+ break;
+ if (!l)
+ next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
+ } else {
+ /* Have to look at its predecessors */
+ if (depth < 5)
+ collect_pred_seq_points (in_bb, ins, next, depth + 1);
+ }
+ }
+}
+
+void
+mono_save_seq_point_info (MonoCompile *cfg)
+{
+ MonoBasicBlock *bb;
+ GSList *bb_seq_points, *l;
+ MonoInst *last;
+ MonoDomain *domain = cfg->domain;
+ int i;
+ GSList **next;
+ SeqPoint* seq_points;
+ GByteArray* array;
+ gboolean has_debug_data = cfg->gen_seq_points_debug_data;
+
+ if (!cfg->seq_points)
+ return;
+
+ seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
+
+ for (i = 0; i < cfg->seq_points->len; ++i) {
+ SeqPoint *sp = &seq_points [i];
+ MonoInst *ins = g_ptr_array_index (cfg->seq_points, i);
+
+ sp->il_offset = ins->inst_imm;
+ sp->native_offset = ins->inst_offset;
+ if (ins->flags & MONO_INST_NONEMPTY_STACK)
+ sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
+
+ /* Used below */
+ ins->backend.size = i;
+ }
+
+ if (has_debug_data) {
+ /*
+ * For each sequence point, compute the list of sequence points immediately
+ * following it, this is needed to implement 'step over' in the debugger agent.
+ */
+ next = g_new0 (GSList*, cfg->seq_points->len);
+ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+ bb_seq_points = g_slist_reverse (bb->seq_points);
+ last = NULL;
+ for (l = bb_seq_points; l; l = l->next) {
+ MonoInst *ins = l->data;
+
+ if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
+ /* Used to implement method entry/exit events */
+ continue;
+ if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
+ continue;
+
+ if (last != NULL) {
+ /* Link with the previous seq point in the same bb */
+ next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
+ } else {
+ /* Link with the last bb in the previous bblocks */
+ collect_pred_seq_points (bb, ins, next, 0);
+ }
+
+ last = ins;
+ }
+
+ if (bb->last_ins && bb->last_ins->opcode == OP_ENDFINALLY && bb->seq_points) {
+ MonoBasicBlock *bb2;
+ MonoInst *endfinally_seq_point = NULL;
+
+ /*
+ * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
+ */
+ l = g_slist_last (bb->seq_points);
+ if (l) {
+ endfinally_seq_point = l->data;
+
+ for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
+ GSList *l = g_slist_last (bb2->seq_points);
+
+ if (l) {
+ MonoInst *ins = l->data;
+
+ if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
+ next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
+ }
+ }
+ }
+ }
+ }
+
+ if (cfg->verbose_level > 2) {
+ printf ("\nSEQ POINT MAP: \n");
+
+ for (i = 0; i < cfg->seq_points->len; ++i) {
+ SeqPoint *sp = &seq_points [i];
+ GSList *l;
+
+ if (!next [i])
+ continue;
+
+ printf ("\tIL0x%x ->", sp->il_offset);
+ for (l = next [i]; l; l = l->next) {
+ int next_index = GPOINTER_TO_UINT (l->data);
+ printf (" IL0x%x", seq_points [next_index].il_offset);
+ }
+ printf ("\n");
+ }
+ }
+ }
+
+ array = g_byte_array_new ();
+
+ { /* Add sequence points to seq_point_info */
+ SeqPoint zero_seq_point = {0};
+ SeqPoint* last_seq_point = &zero_seq_point;
+
+ for (i = 0; i < cfg->seq_points->len; ++i) {
+ SeqPoint *sp = &seq_points [i];
+ GSList* next_list = NULL;
+
+ if (has_debug_data)
+ next_list = next[i];
+
+ if (seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
+ last_seq_point = sp;
+
+ if (has_debug_data)
+ g_slist_free (next [i]);
+ }
+ }
+
+ if (has_debug_data)
+ g_free (next);
+
+ cfg->seq_point_info = seq_point_info_new (array->len, TRUE, array->data, has_debug_data);
+
+ g_byte_array_free (array, TRUE);
+
+ // FIXME: dynamic methods
+ if (!cfg->compile_aot) {
+ mono_domain_lock (domain);
+ // FIXME: How can the lookup succeed ?
+ if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
+ g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
+ mono_domain_unlock (domain);
+ }
+
+ g_ptr_array_free (cfg->seq_points, TRUE);
+ cfg->seq_points = NULL;
+}
+
+MonoSeqPointInfo*
+get_seq_points (MonoDomain *domain, MonoMethod *method)
+{
+ MonoSeqPointInfo *seq_points;
+
+ mono_domain_lock (domain);
+ seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+ if (!seq_points && method->is_inflated) {
+ /* generic sharing + aot */
+ seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (method));
+ if (!seq_points)
+ seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mini_get_shared_method (method));
+ }
+ mono_domain_unlock (domain);
+
+ return seq_points;
+}
+
+static gboolean
+seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
+{
+ SeqPointIterator it;
+ seq_point_iterator_init (&it, info);
+ while (seq_point_iterator_next (&it)) {
+ if (it.seq_point.native_offset >= native_offset) {
+ memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
+{
+ SeqPoint prev_seq_point;
+ gboolean is_first = TRUE;
+ SeqPointIterator it;
+ seq_point_iterator_init (&it, info);
+ while (seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
+ memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
+ is_first = FALSE;
+ }
+
+ if (!is_first && prev_seq_point.native_offset <= native_offset) {
+ memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
+{
+ SeqPointIterator it;
+ seq_point_iterator_init (&it, info);
+ while (seq_point_iterator_next (&it)) {
+ if (it.seq_point.il_offset == il_offset) {
+ memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * find_next_seq_point_for_native_offset:
+ *
+ * Find the first sequence point after NATIVE_OFFSET.
+ */
+gboolean
+find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
+{
+ MonoSeqPointInfo *seq_points;
+
+ seq_points = get_seq_points (domain, method);
+ if (!seq_points) {
+ if (info)
+ *info = NULL;
+ return FALSE;
+ }
+ if (info)
+ *info = seq_points;
+
+ return seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
+}
+
+/*
+ * find_prev_seq_point_for_native_offset:
+ *
+ * Find the first sequence point before NATIVE_OFFSET.
+ */
+gboolean
+find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
+{
+ MonoSeqPointInfo *seq_points;
+
+ seq_points = get_seq_points (domain, method);
+ if (!seq_points) {
+ if (info)
+ *info = NULL;
+ return FALSE;
+ }
+ if (info)
+ *info = seq_points;
+
+ return seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
+}
+
+/*
+ * find_seq_point:
+ *
+ * Find the sequence point corresponding to the IL offset IL_OFFSET, which
+ * should be the location of a sequence point.
+ */
+gboolean
+find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
+{
+ MonoSeqPointInfo *seq_points;
+
+ seq_points = get_seq_points (domain, method);
+ if (!seq_points) {
+ if (info)
+ *info = NULL;
+ return FALSE;
+ }
+ if (info)
+ *info = seq_points;
+
+ return seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
+}
+
+void
+seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
+{
+ int i;
+ guint8* ptr;
+ SeqPointIterator it;
+ GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
+ SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
+
+ g_assert (info_inflated.has_debug_data);
+
+ seq_point_iterator_init (&it, info);
+ while (seq_point_iterator_next (&it))
+ g_array_append_vals (seq_points, &it.seq_point, 1);
+
+ ptr = info_inflated.data + sp.next_offset;
+ for (i = 0; i < sp.next_len; i++) {
+ int next_index;
+ next_index = decode_var_int (ptr, &ptr);
+ g_assert (next_index < seq_points->len);
+ memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
+ }
+
+ g_array_free (seq_points, TRUE);
+}
+
+gboolean
+seq_point_iterator_next (SeqPointIterator* it)
+{
+ if (it->ptr >= it->end)
+ return FALSE;
+
+ it->ptr += seq_point_read (&it->seq_point, it->ptr, it->begin, it->has_debug_data);
+
+ return TRUE;
+}
+
+void
+seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
+{
+ SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
+ it->ptr = info_inflated.data;
+ it->begin = info_inflated.data;
+ it->end = it->begin + info_inflated.len;
+ it->has_debug_data = info_inflated.has_debug_data;
+ memset(&it->seq_point, 0, sizeof(SeqPoint));
+}
+
+int
+seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
+{
+ guint8* buffer0 = buffer;
+ SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
+
+ memcpy (buffer, &info_inflated.has_debug_data, 1);
+ buffer++;
+
+ //Write sequence points
+ encode_var_int (buffer, &buffer, info_inflated.len);
+ memcpy (buffer, info_inflated.data, info_inflated.len);
+ buffer += info_inflated.len;
+
+ return buffer - buffer0;
+}
+
+int
+seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
+{
+ guint8* buffer0 = buffer;
+ int size;
+ gboolean has_debug_data;
+
+ memcpy (&has_debug_data, buffer, 1);
+ buffer++;
+
+ size = decode_var_int (buffer, &buffer);
+ (*info) = seq_point_info_new (size, copy, buffer, has_debug_data);
+ buffer += size;
+
+ return buffer - buffer0;
+}
+
+/*
+ * Returns the maximum size of mono_seq_point_info_write.
+ */
+int
+seq_point_info_get_write_size (MonoSeqPointInfo* info)
+{
+ SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
+
+ //4 is the maximum size required to store the size of the data.
+ //1 is the byte used to store has_debug_data.
+ int size = 4 + 1 + info_inflated.len;
+
+ return size;
+}
--- /dev/null
+/*
+ * Copyright 2014 Xamarin Inc
+ */
+
+#ifndef __MONO_SEQ_POINTS_H__
+#define __MONO_SEQ_POINTS_H__
+
+#define MONO_SEQ_POINT_FLAG_NONEMPTY_STACK 1
+#define MONO_SEQ_POINT_FLAG_EXIT_IL 2
+
+/* IL offsets used to mark the sequence points belonging to method entry/exit events */
+#define METHOD_ENTRY_IL_OFFSET -1
+#define METHOD_EXIT_IL_OFFSET 0xffffff
+
+/* Native offset used to mark seq points in dead code */
+#define SEQ_POINT_NATIVE_OFFSET_DEAD_CODE -1
+
+typedef struct {
+ int il_offset, native_offset, flags;
+ /* Offset of indexes of successor sequence points on the compressed buffer */
+ int next_offset;
+ /* Number of entries in next */
+ int next_len;
+} SeqPoint;
+
+typedef struct MonoSeqPointInfo {
+} MonoSeqPointInfo;
+
+typedef struct {
+ SeqPoint seq_point;
+ guint8* ptr;
+ guint8* begin;
+ guint8* end;
+ gboolean has_debug_data;
+} SeqPointIterator;
+
+void
+seq_point_info_free (gpointer info);
+
+void
+mono_save_seq_point_info (MonoCompile *cfg);
+
+MonoSeqPointInfo*
+get_seq_points (MonoDomain *domain, MonoMethod *method);
+
+gboolean
+find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point);
+
+gboolean
+find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point);
+
+gboolean
+find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point);
+
+gboolean
+seq_point_iterator_next (SeqPointIterator* it);
+
+void
+seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info);
+
+void
+seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next);
+
+int
+seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer);
+
+int
+seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy);
+
+int
+seq_point_info_get_write_size (MonoSeqPointInfo* info);
+
+#endif /* __MONO_SEQ_POINTS_H__ */
\ No newline at end of file