| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Globalization;
- using System.Runtime.InteropServices;
- public enum Record {
- MustNotHoldAny,
- MustNotHoldOne,
- MustHoldOne,
- LockAcquired,
- LockReleased
- }
- public struct LockRecord {
- public SimLock lk;
- public string frame;
- public LockRecord (SimLock lk, string frame) {
- this.lk = lk;
- this.frame = frame;
- }
- }
- public class SimThread
- {
- int thread;
- List <LockRecord> locks = new List <LockRecord> ();
- public SimThread (int t)
- {
- this.thread = t;
- }
- public bool HoldsLock (SimLock lk) {
- foreach (var l in locks) {
- if (l.lk == lk)
- return true;
- }
- return false;
- }
- public int HoldCount (SimLock lk) {
- int res = 0;
- foreach (var l in locks)
- if (l.lk == lk)
- ++res;
- return res;
- }
- public void Lock (SimLock lk, string frame) {
- foreach (LockRecord lr in locks) {
- if (lk.WarnAbout (this, lr.lk))
- Console.WriteLine ("WARNING: tried to acquire lock {0} at {1} while holding {2} at {3}: {4}", lk, frame, lr.lk, lr.frame, lk.GetWarningMessage (this, lr.lk));
- else if (!lk.IsValid (this, lr.lk))
- Console.WriteLine ("ERROR: tried to acquire lock {0} at {1} while holding {2} at {3}: {4}", lk, frame, lr.lk, lr.frame, lk.GetErrorMessage (this, lr.lk));
- }
- locks.Add (new LockRecord (lk, frame));
- }
- public void Release (SimLock lk, string frame) {
- if (locks.Count == 0) {
- Console.WriteLine ("ERROR: released lock {0} at {1} while holding no locks!", lk, frame);
- return;
- }
- LockRecord top = locks [locks.Count - 1];
- if (top.lk != lk && !(lk.IsGlobalLock && HoldCount (lk) > 1)) {
- Console.WriteLine ("WARNING: released lock {0} at {1} out of order with {2} at {3}!", lk, frame, top.lk, top.frame);
- }
- for (int i = locks.Count -1; i >= 0; --i) {
- if (locks [i].lk == lk) {
- locks.RemoveAt (i);
- break;
- }
- }
- }
- }
- /*
- LOCK RULES
- Simple locks:
- Can be acquired at any point regardless of which locks are taken or not.
- No other locks can be acquired or released while holding a simple lock.
- Reentrancy is not recomended. (warning)
- Simple locks are leaf locks on the lock lattice.
- Complex locks:
- Must respect locking order, which form a lattice.
- IOW, to take a given lock, only it's parents might have been taken.
- Reentrancy is ok.
- Locks around resources count as separate instances of the hierarchy.
- Global locks:
- Must respect locking order.
- Must be the at the botton of the locking lattice.
- Can be taken out-of-order by other locks given that it was previously acquired.
- Adding global locks is not to be taken lightly.
- The current lock hierarchy:
- loader lock
- domain lock
- domain jit lock
- simple locks
- Examples:
- You can take the loader lock without holding a domain lock.
- You cannot take a domain lock if the loader lock is held.
- You cannot take a domain lock while holding the lock to another domain.
- */
- public enum Lock {
- Invalid,
- LoaderLock,
- ImageDataLock,
- DomainLock,
- DomainAssembliesLock,
- DomainJitCodeHashLock,
- }
- public class SimLock
- {
- Lock kind;
- int id;
- public SimLock (Lock kind, int id) {
- this.kind = kind;
- this.id = id;
- }
- static int GetLockOrder (Lock kind) {
- switch (kind) {
- case Lock.LoaderLock:
- return 0;
- case Lock.DomainLock:
- return 1;
- case Lock.DomainJitCodeHashLock:
- return 2;
- default:
- return 3;
- }
- }
- bool IsParent (SimLock other) {
- return GetLockOrder (kind) > GetLockOrder (other.kind);
- }
- public bool IsSimpleLock {
- get { return GetLockOrder (kind) == 3; }
- }
- public bool IsGlobalLock {
- get { return kind == Lock.LoaderLock; }
- }
- /*locked is already owned by the thread, 'this' is the new one*/
- bool Compare (SimThread thread, SimLock locked, out bool isWarning, out string msg)
- {
- isWarning = false;
- msg = null;
- if (locked != this) {
- if (!IsParent (locked)) {
- if (IsGlobalLock) { /*acquiring a global lock*/
- if (!thread.HoldsLock (this)) { /*does the thread alread hold it?*/
- msg = "Acquired a global lock after a regular lock without having it before.";
- return false;
- }
- } else {
- msg = "Hierarchy violation.";
- return false;
- }
- }
- } else if (IsSimpleLock) {
- msg = "Avoid taking simple locks recursively";
- isWarning = true;
- return false;
- }
- return true;
- }
- public bool IsValid (SimThread thread, SimLock locked) {
- bool warn;
- string msg;
- return Compare (thread, locked, out warn, out msg);
- }
- public bool WarnAbout (SimThread thread, SimLock locked) {
- bool warn;
- string msg;
- Compare (thread, locked, out warn, out msg);
- return warn;
- }
- public string GetWarningMessage (SimThread thread, SimLock locked) {
- bool warn;
- string msg;
- Compare (thread, locked, out warn, out msg);
- return warn ? msg : null;
- }
- public string GetErrorMessage (SimThread thread, SimLock locked) {
- bool warn;
- string msg;
- bool res = Compare (thread, locked, out warn, out msg);
- return !res && !warn ? msg : null;
- }
- public override string ToString () {
- return String.Format ("{0}", kind);
- }
- }
- public class LockSimulator
- {
- static Dictionary <int, SimThread> threads = new Dictionary <int, SimThread> ();
- static Dictionary <int, SimLock> locks = new Dictionary <int, SimLock> ();
- SymbolTable syms;
- public LockSimulator (SymbolTable s) { this.syms = s; }
- SimLock GetLock (Trace t) {
- if (locks.ContainsKey (t.lockPtr))
- return locks [t.lockPtr];
- else {
- return locks [t.lockPtr] = new SimLock (t.lockKind, t.lockPtr);
- }
- }
- SimThread GetThread (Trace t) {
- if (threads.ContainsKey (t.thread))
- return threads [t.thread];
- else
- return threads [t.thread] = new SimThread (t.thread);
- }
- public void PlayBack (IEnumerable<Trace> traces) {
- foreach (var t in traces) {
- SimThread thread = GetThread (t);
- SimLock lk = GetLock (t);
- string frame = t.GetUsefullTopTrace (this.syms);
- switch (t.record) {
- case Record.MustNotHoldAny:
- case Record.MustNotHoldOne:
- case Record.MustHoldOne:
- throw new Exception ("not supported");
- case Record.LockAcquired:
- thread.Lock (lk, frame);
- break;
- case Record.LockReleased:
- thread.Release (lk, frame);
- break;
- default:
- throw new Exception ("Invalid trace record: "+t.record);
- }
- }
- }
- }
- public class Trace {
- public int thread;
- public Record record;
- public Lock lockKind;
- public int lockPtr;
- int[] frames;
- static readonly string[] BAD_FRAME_METHODS = new string[] {
- "mono_loader_lock",
- "mono_loader_unlock",
- "mono_image_lock",
- "mono_image_unlock",
- };
- public Trace (string[] fields) {
- thread = fields [0].ParseHex ();
- record = (Record)fields [1].ParseDec ();
- lockKind = (Lock)fields [2].ParseDec ();
- lockPtr = fields [3].ParseHex ();
- frames = new int [fields.Length - 4];
- for (int i = 0; i < frames.Length; ++i)
- frames [i] = fields [i + 4].ParseHex ();
- }
- public void Dump (SymbolTable table) {
- Console.WriteLine ("{0:x} {1} {2} {3:x}", thread, record, lockKind, lockPtr);
- for (int i = 0; i < frames.Length; ++i)
- Console.WriteLine ("\t{0}", table.Translate (frames [i]));
- }
- public string GetUsefullTopTrace (SymbolTable syms) {
- for (int i = 0; i < frames.Length; ++i) {
- string str = syms.Translate (frames [i]);
- bool ok = true;
- for (int j = 0; j < BAD_FRAME_METHODS.Length; ++j) {
- if (str.IndexOf (BAD_FRAME_METHODS [j]) >= 0) {
- ok = false;
- break;
- }
- }
- if (ok)
- return str;
- }
- return "[unknown]";
- }
- }
- public class Symbol : IComparable<Symbol>
- {
- public int offset;
- public int size;
- public string name;
- public Symbol (int o, int size, string n) {
- this.offset = o;
- this.size = size;
- this.name = n;
- }
- public int CompareTo(Symbol other) {
- return offset - other.offset;
- }
- }
- public interface SymbolTable {
- string Translate (int offset);
- }
- public class OsxSymbolTable : SymbolTable
- {
- Symbol[] table;
- const int MAX_FUNC_SIZE = 0x20000;
- public OsxSymbolTable (string binary) {
- Load (binary);
- }
- void Load (string binary) {
- ProcessStartInfo psi = new ProcessStartInfo ("gobjdump", "-t "+binary);
- psi.UseShellExecute = false;
- psi.RedirectStandardOutput = true;
- var proc = Process.Start (psi);
- var list = new List<Symbol> ();
- string line;
- while ((line = proc.StandardOutput.ReadLine ()) != null) {
- string[] fields = line.Split(new char[] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries);
- if (fields.Length < 4)
- continue;
- if (!(fields [1].Equals ("g") || fields [1].Equals ("d")))
- continue;
- if (!fields [2].Equals ("*UND*"))
- continue;
- int offset = fields [0].ParseHex ();
- string name = fields [3];
- if (offset != 0)
- list.Add (new Symbol (offset, 0, name));
- }
- table = new Symbol [list.Count];
- list.CopyTo (table, 0);
- Array.Sort (table);
- }
- public string Translate (int offset) {
- Symbol sym = null;
- int res = Array.BinarySearch (table, new Symbol (offset, 0, null));
- if (res >= 0)
- return table [res].name;
- res = ~res;
- if (res >= table.Length)
- sym = table [table.Length - 1];
- else if (res != 0)
- sym = table [res - 1];
-
- if (sym != null) {
- int size = Math.Max (sym.size, 10);
- if (offset - sym.offset < size)
- return sym.name;
- }
- return String.Format ("[{0:x}]", offset);
- }
- }
- public class LinuxSymbolTable : SymbolTable
- {
- Symbol[] table;
- const int MAX_FUNC_SIZE = 0x20000;
- public LinuxSymbolTable (string binary) {
- Load (binary);
- }
- void Load (string binary) {
- ProcessStartInfo psi = new ProcessStartInfo ("objdump", "-t "+binary);
- psi.UseShellExecute = false;
- psi.RedirectStandardOutput = true;
- var proc = Process.Start (psi);
- var list = new List<Symbol> ();
- string line;
- while ((line = proc.StandardOutput.ReadLine ()) != null) {
- string[] fields = line.Split(new char[] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries);
- if (fields.Length < 6)
- continue;
- if (fields [3] != ".text" || fields [2] != "F")
- continue;
- int offset = fields [0].ParseHex ();
- int size = fields [4].ParseHex ();
- string name = fields [fields.Length - 1];
- if (offset != 0)
- list.Add (new Symbol (offset, size, name));
- }
- table = new Symbol [list.Count];
- list.CopyTo (table, 0);
- Array.Sort (table);
- }
- public string Translate (int offset) {
- Symbol sym = null;
- int res = Array.BinarySearch (table, new Symbol (offset, 0, null));
- if (res >= 0)
- return table [res].name;
- res = ~res;
- if (res >= table.Length)
- sym = table [table.Length - 1];
- else if (res != 0)
- sym = table [res - 1];
- if (sym != null && offset - sym.offset < MAX_FUNC_SIZE)
- return sym.name;
- return String.Format ("[{0:x}]", offset);
- }
- }
- public class TraceDecoder
- {
- string file;
- public TraceDecoder (string file) {
- this.file = file;
- }
- public IEnumerable<Trace> GetTraces () {
- using (StreamReader reader = new StreamReader (file)) {
- string line;
- while ((line = reader.ReadLine ()) != null) {
- string[] fields = line.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
- if (fields.Length >= 7) {
- yield return new Trace (fields);
- }
- }
- }
- }
- }
- public class Driver
- {
- [DllImport ("libc")]
- static extern int uname (IntPtr buf);
- static bool IsOSX ()
- {
- bool isOsx = false;
- IntPtr buf = Marshal.AllocHGlobal (8192);
- if (uname (buf) == 0) {
- string os = Marshal.PtrToStringAnsi (buf);
- isOsx = os == "Darwin";
- }
- Marshal.FreeHGlobal (buf);
- return isOsx;
- }
- static void Main (string[] args) {
- SymbolTable syms;
- if (args.Length != 2) {
- Console.WriteLine ("usage: LockTracerDecoder.exe /path/to/mono /path/to/locks.pid");
- return;
- }
- if (IsOSX ())
- syms = new OsxSymbolTable (args [0]);
- else
- syms = new LinuxSymbolTable (args [0]);
- var decoder = new TraceDecoder (args [1]);
- var sim = new LockSimulator (syms);
- sim.PlayBack (decoder.GetTraces ());
- }
- }
- public static class Utils
- {
- public static int ParseHex (this string number) {
- while (number.Length > 1 && (number [0] == '0' || number [0] == 'x' || number [0] == 'X'))
- number = number.Substring (1);
- return int.Parse (number, NumberStyles.HexNumber);
- }
- public static int ParseDec (this string number) {
- while (number.Length > 1 && number [0] == '0')
- number = number.Substring (1);
- return int.Parse (number);
- }
- }
|