/// <summary> /// Provides default binding for performing a call on the specified meta objects. /// </summary> /// <param name="signature">The signature describing the call</param> /// <param name="target">The meta object to be called.</param> /// <param name="args"> /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. /// </param> /// <param name="resolverFactory">Overload resolver factory.</param> /// <param name="errorSuggestion">The result should the object be uncallable.</param> /// <returns>A MetaObject representing the call or the error.</returns> public DynamicMetaObject Call(CallSignature signature, DynamicMetaObject errorSuggestion, OverloadResolverFactory resolverFactory,ClrMethod method, DynamicMetaObject target, params DynamicMetaObject[] args) { ContractUtils.RequiresNotNullItems(args, "args"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); TargetInfo targetInfo = GetTargetInfo(method, target, args); if (targetInfo != null) { // we're calling a well-known MethodBase DynamicMetaObject res = MakeMetaMethodCall(signature, resolverFactory, targetInfo); if (res.Expression.Type.IsValueType) { if (res.Expression.Type == Types.Void) res = new DynamicMetaObject( Expression.Block(Types.Object[0], res.Expression, Expression.Constant(null)), res.Restrictions ); else res = new DynamicMetaObject( Expression.Convert(res.Expression, typeof(object)), res.Restrictions ); } return res; } else { // we can't call this object return errorSuggestion ?? MakeCannotCallRule(target, target.GetLimitType()); } }
public override SourceLocation GetSourceInformation(ClrMethod method, int ilOffset) { if (method == null) throw new ArgumentNullException("method"); if (method.Type != null && method.Type.Module != this) throw new InvalidOperationException("Method not in this module."); return GetSourceInformation(method.MetadataToken, ilOffset); }
/// <summary> /// Gets a TargetInfo object for performing a call on this object. /// </summary> private TargetInfo GetTargetInfo(ClrMethod method, DynamicMetaObject target, DynamicMetaObject[] args) { Debug.NotNull(method); Debug.Assert(target.HasValue); object objTarget = target.Value; List<MethodBase> foundTargets = new List<MethodBase>(method.Overloads.Count); foreach (PHP.Core.Reflection.ClrMethod.Overload overload in method.Overloads) { foundTargets.Add(overload.Method); } return new TargetInfo(target, args, target.Restrictions, foundTargets.ToArray()); }
/// <summary> /// Used by full-reflect (<see cref="DTypeDesc"/>). /// </summary> internal static ClrMethod/*!*/ CreateConstructor(ClrTypeDesc/*!*/ declaringType) { ClrMethod result; if (declaringType is ClrDelegateDesc) { // the real constructor should not be accessible from PHP code result = new ClrMethod(Name.ClrCtorName, declaringType, PhpMemberAttributes.Constructor, 1, false); result.ClrMethodDesc.ArglessStub = new RoutineDelegate(declaringType._NoConstructorErrorStub); } else { ConstructorInfo[] realOverloads = declaringType.RealType.GetConstructors( BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); //// TODO (less restrictive?): PhpMemberAttributes attrs = PhpMemberAttributes.Constructor; int visible_count = 0; for (int i = 0; i < realOverloads.Length; i++) { if (ClrTypeDesc.IsMethodVisible(realOverloads[i])) visible_count++; } if (declaringType.RealType.IsValueType) // add an empty .ctor visible_count++; if (visible_count > 0) { result = new ClrMethod(Name.ClrCtorName, declaringType, attrs, visible_count, false); foreach (MethodBase real_overload in realOverloads) { if (ClrTypeDesc.IsMethodVisible(real_overload)) { Overload overload; result.AddOverload(real_overload, out overload); } } if (declaringType.RealType.IsValueType) // add an empty .ctor { Overload overload; result.AddOverload(BuildDefaultValueCtor(declaringType.RealType), out overload); } } else { result = new ClrMethod(Name.ClrCtorName, declaringType, PhpMemberAttributes.Constructor, 1, false); result.ClrMethodDesc.ArglessStub = new RoutineDelegate(declaringType._NoConstructorErrorStub); } } return result; }
public DesktopStackFrame(DesktopRuntimeBase runtime, ulong sp, string method, ClrMethod innerMethod) { _runtime = runtime; _sp = sp; _frameName = method ?? "Unknown"; _type = ClrStackFrameType.Runtime; _method = innerMethod; }
private static void TryTranslateAddressToName(Instruction instruction, State state, int depth, ClrMethod currentMethod) { var runtime = state.Runtime; if (!TryGetReferencedAddress(instruction, (uint)runtime.PointerSize, out ulong address)) { return; } if (state.AddressToNameMapping.ContainsKey(address)) { return; } var jitHelperFunctionName = runtime.GetJitHelperFunctionName(address); if (!string.IsNullOrEmpty(jitHelperFunctionName)) { state.AddressToNameMapping.Add(address, jitHelperFunctionName); return; } var methodTableName = runtime.GetMethodTableName(address); if (!string.IsNullOrEmpty(methodTableName)) { state.AddressToNameMapping.Add(address, $"MT_{methodTableName}"); return; } var methodDescriptor = runtime.GetMethodByHandle(address); if (!(methodDescriptor is null)) { state.AddressToNameMapping.Add(address, $"MD_{methodDescriptor.GetFullSignature()}"); return; } var method = runtime.GetMethodByAddress(address); if (method is null && (address & ((uint)runtime.PointerSize - 1)) == 0) { if (runtime.ReadPointer(address, out ulong newAddress) && newAddress > ushort.MaxValue) { method = runtime.GetMethodByAddress(newAddress); } } if (method is null) { return; } if (method.NativeCode == currentMethod.NativeCode && method.GetFullSignature() == currentMethod.GetFullSignature()) { return; // in case of a call which is just a jump within the method or a recursive call } if (!state.HandledMethods.Contains(method)) { state.Todo.Enqueue(new MethodInfo(method, depth + 1)); } var methodName = method.GetFullSignature(); if (!methodName.Any(c => c == '.')) // the method name does not contain namespace and type name { methodName = $"{method.Type.Name}.{method.GetFullSignature()}"; } state.AddressToNameMapping.Add(address, methodName); }
public override SourceLocation GetSourceInformation(ClrMethod method, int ilOffset) { return(null); }
extern private static Delegate CreateBlankDelegate (Type type, ClrMethod method);
/// <summary> /// If the adapted method is a generic method definition with the supplied number and /// type of parameters, closes the method with the supplied arguments /// </summary> /// <param name="args"></param> /// <returns></returns> public Option <ClrMethod> CloseGenericMethod(params ClrType[] args) => Try(() => ClrMethod.Get(ReflectedElement.MakeGenericMethod(array(args, a => a.ReflectedElement))));
/// <summary> /// Provides default binding for performing a call on the specified meta objects. /// </summary> /// <param name="signature">The signature describing the call</param> /// <param name="target">The meta object to be called.</param> /// <param name="args"> /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. /// </param> /// <param name="resolverFactory">Overload resolver factory.</param> /// <param name="errorSuggestion">The result should the object be uncallable.</param> /// <returns>A MetaObject representing the call or the error.</returns> public DynamicMetaObject Call(CallSignature signature, DynamicMetaObject errorSuggestion, OverloadResolverFactory resolverFactory, ClrMethod method, DynamicMetaObject target, params DynamicMetaObject[] args) { ContractUtils.RequiresNotNullItems(args, "args"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); TargetInfo targetInfo = GetTargetInfo(method, target, args); if (targetInfo != null) { // we're calling a well-known MethodBase DynamicMetaObject res = MakeMetaMethodCall(signature, resolverFactory, targetInfo); if (res.Expression.Type.IsValueType) { if (res.Expression.Type == Types.Void) { res = new DynamicMetaObject( Expression.Block(Types.Object[0], res.Expression, Expression.Constant(null)), res.Restrictions ); } else { res = new DynamicMetaObject( Expression.Convert(res.Expression, typeof(object)), res.Restrictions ); } } return(res); } else { // we can't call this object return(errorSuggestion ?? MakeCannotCallRule(target, target.GetLimitType())); } }
public DelegateTargetInformation(ulong address, ClrDumpType clrDumpType, ClrMethod methInfo) : this(address, clrDumpType) { this.methInfo = methInfo; }
/// <summary> /// Defines a method signature based on relected metadata /// </summary> /// <param name="method">The reflected method</param> /// <returns></returns> public static MethodSignature SpecifyMethodSignature(this ClrMethod method) => new MethodSignature(method.Name, method.ReturnType.ValueOrDefault().SpecifyClrTypeReference(), map(method.Parameters, p => p.Specify()) );
public DynamicMetaObject CallClrMethod(ClrMethod method, DynamicMetaObject target, DynamicMetaObject[] args) { CallSignature signature = new CallSignature(args.Length); return(Call(signature, null, new DefaultOverloadResolverFactory(this), method, target, args)); }
private static void PrintILToNativeOffsets(ClrMethod method, ulong startAddress, IList<string> lines) { Console.WriteLine("IL -> Native Offsets:"); Console.WriteLine("\t" + String.Join("\n\t", @method.ILOffsetMap)); foreach (ILToNativeMap il in @method.ILOffsetMap) { SourceLocation sourceLocation = null; try { Console.WriteLine( "\nPrintILToNativeOffsets, il.Offset: {0,10} ({1,3:N0}), il.StartAddress: {2} ({2,8:X8})", "0x" + il.ILOffset.ToString("X2"), il.ILOffset, il.StartAddress); if (il.ILOffset < 0) // FFFF FFFX is 4,294,967,280 continue; sourceLocation = @method.GetSourceLocationForOffset(il.StartAddress); } catch (Exception ex) { Console.WriteLine(ex.Message); continue; } var sourceInfo = "<UNKNOWN>"; if (sourceLocation != null) { if (sourceLocation.LineNumber == sourceLocation.LineNumberEnd) { sourceInfo = String.Format("{0}:{1} (Columns {2}->{3})", Path.GetFileName(sourceLocation.FilePath), sourceLocation.LineNumber, sourceLocation.ColStart, sourceLocation.ColEnd); } else { sourceInfo = String.Format("{0}: {1}->{2} ({3}->{4})", Path.GetFileName(sourceLocation.FilePath), sourceLocation.LineNumber, sourceLocation.LineNumberEnd, sourceLocation.ColStart, sourceLocation.ColEnd); } } Console.WriteLine("{0,10} ({1,3:N0}) - [{2:X8}-{3:X8} ({4:X8}-{5:X8})] {6}", "0x" + il.ILOffset.ToString("X2"), il.ILOffset, il.StartAddress - startAddress, il.EndAddress - startAddress, il.StartAddress, il.EndAddress, sourceInfo); if (sourceLocation != null && il.ILOffset >= 0) { var indent = 7; Console.WriteLine("{0,6}:{1}", sourceLocation.LineNumber, lines[sourceLocation.LineNumber - 1]); Console.WriteLine(new string(' ', sourceLocation.ColStart - 1 + indent) + new string('*', sourceLocation.ColEnd - sourceLocation.ColStart)); } } }
private static void PrintILToNativeOffsetAlternative(ClrMethod method, IList<string> lines) { DesktopModule module = (DesktopModule) @method.Type.Module; if (!module.IsPdbLoaded) { // Have to load the Pdb, if it's not already loaded!! string val = module.TryDownloadPdb(null); if (val != null) module.LoadPdb(val); } foreach (var location in module.GetSourceLocationsForMethod(@method.MetadataToken)) { ILOffsetSourceLocation ILLocation = location; var ilMaps = @method.ILOffsetMap.Where(il => il.ILOffset == ILLocation.ILOffset); Console.WriteLine("{0:X8} -> {1}:{2}", location.ILOffset, Path.GetFileName(location.SourceLocation.FilePath), location.SourceLocation.LineNumber); Console.WriteLine(" " + String.Join("\n ", ilMaps.Select( ilMap => String.Format("[{0:X8}-{1:X8} ({2:X8}-{3:X8})] ILOffset: {4:X2}", ilMap.StartAddress - @method.NativeCode, ilMap.EndAddress - @method.NativeCode, ilMap.StartAddress, ilMap.EndAddress, ilMap.ILOffset)))); var indent = 7; Console.WriteLine("{0,6}:{1}", location.SourceLocation.LineNumber, lines[location.SourceLocation.LineNumber - 1]); Console.WriteLine(new string(' ', location.SourceLocation.ColStart - 1 + indent) + new string('*', location.SourceLocation.ColEnd - location.SourceLocation.ColStart)); } Console.WriteLine(); }
// Sample based on https://github.com/Microsoft/dotnetsamples/blob/master/Microsoft.Diagnostics.Runtime/CLRMD/docs/MachineCode.md // All the common code was copied from the MemStats project "dotnetsamples-master\Microsoft.Diagnostics.Runtime\CLRMD\MemStats\Program.cs" static void Main(string[] args) { string dump, dac; if (!TryParseArgs(args, out dump, out dac)) { Usage(); Environment.Exit(1); } try { // Create a ClrRuntime instance from the dump and dac location. The ClrRuntime // object represents a version of CLR loaded in the process. It contains data // such as the managed threads in the process, the AppDomains in the process, // the managed heap, and so on. //ClrRuntime runtime = CreateRuntime(dump, dac); // 1. Get the ClrType object for the type the method is on // 2. Get the ClrMethod object for the method // 3. Get the offset of the native code // 4. Compute the end address by mapping the IL instruction to addresses // 5. Disassemble the native code contained in that range (not provided by CLRMD) using (DataTarget dt = DataTarget.LoadCrashDump(dump)) { // Boilerplate. //ClrRuntime runtime = dt.CreateRuntime(dt.ClrVersions.Single().TryDownloadDac()); var version = dt.ClrVersions.Single(); //{v4.0.30319.18444} //version.Version = new VersionInfo { Major = 4, Minor = 0, Patch = 30319, Revision = 18444 }; Console.WriteLine("CLR Version: {0} ({1}), Dac: {2}", version.Version, version.Flavor, version.DacInfo); var dacFileName = version.TryDownloadDac(); Console.WriteLine("DacFile: " + Path.GetFileName(dacFileName)); Console.WriteLine("DacPath: " + Path.GetDirectoryName(dacFileName)); ClrRuntime runtime = dt.CreateRuntime(dacFileName); ClrHeap heap = runtime.GetHeap(); PrintDiagnosticInfo(dt, runtime, heap); Console.WriteLine(); // Note heap.GetTypeByName doesn't always get you the type, even if it exists, due to // limitations in the dac private apis that ClrMD is written on. If you have the ClrType // already via other means (heap walking, stack walking, etc), then that's better than // using GetTypeByName: var classNameWithNamespace = "JITterOptimisations.Program"; ClrType @class = heap.GetTypeByName(classNameWithNamespace); // Get the method you are looking for. var signature = "JITterOptimisations.Program.Log(System.ConsoleColor, System.String)"; ClrMethod @method = @class.Methods.Single(m => m.GetFullSignature() == signature); // This is the first instruction of the JIT'ed (or NGEN'ed) machine code. ulong startAddress = @method.NativeCode; // Unfortunately there's not a great way to get the size of the code, or the end address. // This is partly due to the fact that we don't *have* to put all the JIT'ed code into one // contiguous chunk, though I think an implementation detail is that we actually do. // You are supposed to do code flow analysis like "uf" in windbg to find the size, but // in practice you can use the IL to native mapping: ulong endAddress = @method.ILOffsetMap.Select(entry => entry.EndAddress).Max(); var lines = File.ReadAllLines( @"C:\Users\warma11\Documents\Visual Studio 2013\Projects\JITterOptimisations\JITterOptimisations\Program.cs"); PrintILToNativeOffsetAlternative(method, lines); // This doesn't seem to work as expected, using alternative method (above) //PrintILToNativeOffsets(method, startAddress, lines); // So the assembly code for the function is is in the range [startAddress, endAddress] inclusive. var count = (int)endAddress + runtime.PointerSize - (int)startAddress; Console.WriteLine("\nCode startAddress 0x{0:X} -> endAddress 0x{1:X} (inclusive), will read {2} bytes", startAddress, endAddress, count); var bytes = new byte[count]; int bytesRead; runtime.ReadMemory(startAddress, bytes, count, out bytesRead); if (count != bytesRead) { Console.WriteLine("Expected to read {0} bytes, but only read {1}\n", count, bytesRead); } else { Console.WriteLine("Read read {0} bytes, as expected\n", bytesRead); } var fileName = string.Format("result-{0}bit.bin", runtime.PointerSize == 8 ? 64 : 32); if (File.Exists(fileName)) { File.Delete(fileName); } File.WriteAllBytes(fileName, bytes); var filename = @"C:\Users\warma11\Downloads\__GitHub__\dotnetsamples\Microsoft.Diagnostics.Runtime\CLRMD\MachineCode\nasm-2.11.05-win32\ndisasm.exe"; var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var arguments = "-b32 " + Path.Combine(currentFolder, fileName); // +" -o " + startAddress; var disassembly = Disassembler.GetDisassembly(filename, arguments, timeoutMsecs: 250); var assemblyData = Disassembler.ProcessDisassembly(disassembly); } } catch (Exception ex) { Console.WriteLine("Unhandled exception:"); Console.WriteLine(ex); } }
private static void TryTranslateAddressToName(Instruction instruction, State state, int depth, ClrMethod currentMethod) { var runtime = state.Runtime; if (!TryGetReferencedAddress(instruction, (uint)runtime.DataTarget.DataReader.PointerSize, out ulong address)) { return; } if (state.AddressToNameMapping.ContainsKey(address)) { return; } var jitHelperFunctionName = runtime.GetJitHelperFunctionName(address); if (!string.IsNullOrEmpty(jitHelperFunctionName)) { state.AddressToNameMapping.Add(address, jitHelperFunctionName); return; } var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address); if (!string.IsNullOrEmpty(methodTableName)) { state.AddressToNameMapping.Add(address, $"MT_{methodTableName}"); return; } var methodDescriptor = runtime.GetMethodByHandle(address); if (!(methodDescriptor is null)) { state.AddressToNameMapping.Add(address, $"MD_{methodDescriptor.Signature}"); return; } var method = runtime.GetMethodByInstructionPointer(address); if (method is null && (address & ((uint)runtime.DataTarget.DataReader.PointerSize - 1)) == 0) { if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && newAddress > ushort.MaxValue) { method = runtime.GetMethodByInstructionPointer(newAddress); method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, newAddress); } }
private void DisassembleMethod(ClrMethod method) { var module = method.Type.Module; string fileName = module.FileName; AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(fileName); TypeDefinition type = assembly.MainModule.GetType(method.Type.Name); MethodDefinition methodDef = type.Methods.Single( m => m.MetadataToken.ToUInt32() == method.MetadataToken); _context.WriteLine("{0}", method.GetFullSignature()); if (method.ILOffsetMap == null) { return; } var mapByOffset = (from map in method.ILOffsetMap where map.ILOffset >= 0 // prolog is -2, epilog -3 where map.StartAddress <= map.EndAddress orderby map.ILOffset select map).ToArray(); if (mapByOffset.Length == 0) { // The method doesn't have an offset map. Just print the whole thing. PrintInstructions(methodDef.Body.Instructions); } // This is the prologue, looks like it's always there, but it could // also be the only thing that's in the method var prologue = method.ILOffsetMap[0]; if (prologue.ILOffset == -2) // -2 is a magic number for prologue { DisassembleNative(prologue); } for (int i = 0; i < mapByOffset.Length; ++i) { var map = mapByOffset[i]; IEnumerable <Instruction> instructions; if (i == mapByOffset.Length - 1) { instructions = methodDef.Body.Instructions.Where( instr => instr.Offset >= map.ILOffset); } else { instructions = methodDef.Body.Instructions.Where( instr => instr.Offset >= map.ILOffset && instr.Offset < mapByOffset[i + 1].ILOffset); } var sourceLocation = method.GetSourceLocation(map.ILOffset); if (sourceLocation != null) { _context.WriteLine("{0} {1}-{2}:{3}-{4}", sourceLocation.FilePath, sourceLocation.LineNumber, sourceLocation.LineNumberEnd, sourceLocation.ColStart, sourceLocation.ColEnd); for (int line = sourceLocation.LineNumber; line <= sourceLocation.LineNumberEnd; ++line) { var sourceLine = ReadSourceLine(sourceLocation.FilePath, line); if (sourceLine != null) { _context.WriteLine(sourceLine); _context.WriteLine(new string(' ', sourceLocation.ColStart - 1) + new string('^', sourceLocation.ColEnd - sourceLocation.ColStart)); } } } PrintInstructions(instructions); DisassembleNative(map); } var epilogue = method.ILOffsetMap[method.ILOffsetMap.Length - 1]; if (epilogue.ILOffset == -3) // -3 is a magic number for epilog { DisassembleNative(epilogue); } }
public override SourceLocation GetSourceInformation(ClrMethod method, int ilOffset) { return null; }
public static Option <ClrMethod> method(Type declaringType, string name, params Type[] argTypes) => declaringType.MatchMethod(name, argTypes).Map(x => ClrMethod.Get(x));
private void SetThreadWaiters() { HashSet <string> eventTypes = null; List <BlockingObject> blobjs = new List <BlockingObject>(); foreach (DesktopThread thread in _runtime.Threads) { int max = thread.StackTrace.Count; if (max > 10) { max = 10; } blobjs.Clear(); for (int i = 0; i < max; ++i) { DesktopBlockingObject blockingObj = null; ClrMethod method = thread.StackTrace[i].Method; if (method == null) { continue; } ClrType type = method.Type; if (type == null) { continue; } switch (method.Name) { case "AcquireWriterLockInternal": case "FCallUpgradeToWriterLock": case "UpgradeToWriterLock": case "AcquireReaderLockInternal": case "AcquireReaderLock": if (type.Name == "System.Threading.ReaderWriterLock") { blockingObj = FindLocks(thread.StackLimit, thread.StackTrace[i].StackPointer, IsReaderWriterLock); if (blockingObj == null) { blockingObj = FindLocks(thread.StackTrace[i].StackPointer, thread.StackBase, IsReaderWriterLock); } if (blockingObj != null && (blockingObj.Reason == BlockingReason.Unknown || blockingObj.Reason == BlockingReason.None)) { // This should have already been set correctly when the BlockingObject was created. This is just a best-guess. if (method.Name == "AcquireReaderLockInternal" || method.Name == "AcquireReaderLock") { blockingObj.Reason = BlockingReason.WriterAcquired; } else { blockingObj.Reason = BlockingReason.ReaderAcquired; } } } break; case "TryEnterReadLockCore": case "TryEnterReadLock": case "TryEnterUpgradeableReadLock": case "TryEnterUpgradeableReadLockCore": case "TryEnterWriteLock": case "TryEnterWriteLockCore": if (type.Name == "System.Threading.ReaderWriterLockSlim") { blockingObj = FindLocks(thread.StackLimit, thread.StackTrace[i].StackPointer, IsReaderWriterSlim); if (blockingObj == null) { blockingObj = FindLocks(thread.StackTrace[i].StackPointer, thread.StackBase, IsReaderWriterSlim); } if (blockingObj != null && (blockingObj.Reason == BlockingReason.Unknown || blockingObj.Reason == BlockingReason.None)) { // This should have already been set correctly when the BlockingObject was created. This is just a best-guess. if (method.Name == "TryEnterWriteLock" || method.Name == "TryEnterWriteLockCore") { blockingObj.Reason = BlockingReason.ReaderAcquired; } else { blockingObj.Reason = BlockingReason.WriterAcquired; } } } break; case "JoinInternal": case "Join": if (type.Name == "System.Threading.Thread") { if (FindThread(thread.StackLimit, thread.StackTrace[i].StackPointer, out ulong threadAddr, out ClrThread target) || FindThread(thread.StackTrace[i].StackPointer, thread.StackBase, out threadAddr, out target)) { if (!_joinLocks.TryGetValue(target, out blockingObj)) { _joinLocks[target] = blockingObj = new DesktopBlockingObject(threadAddr, true, 0, target, BlockingReason.ThreadJoin); } } } break; case "Wait": case "ObjWait": if (type.Name == "System.Threading.Monitor") { blockingObj = FindMonitor(thread.StackLimit, thread.StackTrace[i].StackPointer); if (blockingObj == null) { blockingObj = FindMonitor(thread.StackTrace[i].StackPointer, thread.StackBase); } blockingObj.Reason = BlockingReason.MonitorWait; } break; case "WaitAny": case "WaitAll": if (type.Name == "System.Threading.WaitHandle") { ulong obj = FindWaitObjects(thread.StackLimit, thread.StackTrace[i].StackPointer, "System.Threading.WaitHandle[]"); if (obj == 0) { obj = FindWaitObjects(thread.StackTrace[i].StackPointer, thread.StackBase, "System.Threading.WaitHandle[]"); } if (obj != 0) { BlockingReason reason = method.Name == "WaitAny" ? BlockingReason.WaitAny : BlockingReason.WaitAll; if (!_waitLocks.TryGetValue(obj, out blockingObj)) { _waitLocks[obj] = blockingObj = new DesktopBlockingObject(obj, true, 0, null, reason); } } } break; case "WaitOne": case "InternalWaitOne": case "WaitOneNative": if (type.Name == "System.Threading.WaitHandle") { if (eventTypes == null) { eventTypes = new HashSet <string> { "System.Threading.Mutex", "System.Threading.Semaphore", "System.Threading.ManualResetEvent", "System.Threading.AutoResetEvent", "System.Threading.WaitHandle", "Microsoft.Win32.SafeHandles.SafeWaitHandle" }; } ulong obj = FindWaitHandle(thread.StackLimit, thread.StackTrace[i].StackPointer, eventTypes); if (obj == 0) { obj = FindWaitHandle(thread.StackTrace[i].StackPointer, thread.StackBase, eventTypes); } if (obj != 0) { if (_waitLocks == null) { _waitLocks = new Dictionary <ulong, DesktopBlockingObject>(); } if (!_waitLocks.TryGetValue(obj, out blockingObj)) { _waitLocks[obj] = blockingObj = new DesktopBlockingObject(obj, true, 0, null, BlockingReason.WaitOne); } } } break; case "TryEnter": case "ReliableEnterTimeout": case "TryEnterTimeout": case "ReliableEnter": case "Enter": if (type.Name == "System.Threading.Monitor") { blockingObj = FindMonitor(thread.StackLimit, thread.StackTrace[i].StackPointer); if (blockingObj != null) { blockingObj.Reason = BlockingReason.Monitor; } } break; } if (blockingObj != null) { bool alreadyEncountered = false; foreach (var blobj in blobjs) { if (blobj.Object == blockingObj.Object) { alreadyEncountered = true; break; } } if (!alreadyEncountered) { blobjs.Add(blockingObj); } } } foreach (DesktopBlockingObject blobj in blobjs) { blobj.AddWaiter(thread); } thread.SetBlockingObjects(blobjs.ToArray()); } }
static string TryEnqueueCalledMethod(string textRepresentation, State state, int depth, ClrMethod currentMethod) { if (!TryGetHexAdress(textRepresentation, out ulong address)) { return(null); // call qword ptr [rax+20h] // needs further research } var method = state.Runtime.GetMethodByAddress(address); if (method == null) // not managed method { return(Errors.NotManagedMethod); } if (method.NativeCode == currentMethod.NativeCode && method.GetFullSignature() == currentMethod.GetFullSignature()) { return(null); // in case of call which is just a jump within the method } if (!state.HandledMetadataTokens.Contains(method.MetadataToken)) { state.Todo.Enqueue(new MethodInfo(method, depth + 1)); } return(method.GetFullSignature()); }
public DesktopStackFrame(DesktopRuntimeBase runtime, DesktopThread thread, ulong sp, string method, ClrMethod innerMethod) { _runtime = runtime; _thread = thread; _sp = sp; _frameName = method ?? "Unknown"; _type = ClrStackFrameType.Runtime; _method = innerMethod; }
internal MethodInfo(ClrMethod method, int depth) { Method = method; Depth = depth; }
private static IEnumerable <Asm> Decode(ulong startAddress, uint size, State state, int depth, ClrMethod currentMethod) { byte[] code = new byte[size]; if (!state.Runtime.DataTarget.ReadProcessMemory(startAddress, code, code.Length, out int bytesRead) || bytesRead == 0) { yield break; } var reader = new ByteArrayCodeReader(code, 0, bytesRead); var decoder = Decoder.Create(state.Runtime.PointerSize * 8, reader); decoder.IP = startAddress; while (reader.CanReadByte) { decoder.Decode(out var instruction); TryTranslateAddressToName(instruction, state, depth, currentMethod); yield return(new Asm { InstructionPointer = instruction.IP, Instruction = instruction }); } }
public DynamicMetaObject CallClrMethod(ClrMethod method, DynamicMetaObject target, DynamicMetaObject[] args) { CallSignature signature = new CallSignature(args.Length); return Call(signature,null,new DefaultOverloadResolverFactory(this), method, target, args); }
private static DisassembledMethod CreateEmpty(ClrMethod method, string reason) => DisassembledMethod.Empty(method.GetFullSignature(), method.NativeCode, reason);
private static bool CanBeDisassembled(ClrMethod method) => method.ILOffsetMap.Length > 0 && method.NativeCode > 0;
private void DisassembleMethod(ClrMethod method) { var module = method.Type.Module; string fileName = module.FileName; AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(fileName); TypeDefinition type = assembly.MainModule.GetType(method.Type.Name); MethodDefinition methodDef = type.Methods.Single( m => m.MetadataToken.ToUInt32() == method.MetadataToken); _context.WriteLine("{0}", method.GetFullSignature()); if (method.ILOffsetMap == null) { return; } var mapByOffset = (from map in method.ILOffsetMap where map.ILOffset != -2 where map.StartAddress <= map.EndAddress orderby map.ILOffset select map).ToArray(); if (mapByOffset.Length == 0) { // The method doesn't have an offset map. Just print the whole thing. PrintInstructions(methodDef.Body.Instructions); } // This is the prologue, looks like it's always there, but it could // also be the only thing that's in the method DisassembleNative(method.ILOffsetMap.Single(e => e.ILOffset == -2)); for (int i = 0; i < mapByOffset.Length; ++i) { var map = mapByOffset[i]; IEnumerable <Instruction> instructions; if (i == mapByOffset.Length - 1) { instructions = methodDef.Body.Instructions.Where( instr => instr.Offset >= map.ILOffset); } else { instructions = methodDef.Body.Instructions.Where( instr => instr.Offset >= map.ILOffset && instr.Offset < mapByOffset[i + 1].ILOffset); } // The GetSourceInformation method doesn't attempt to load symbols. if (!module.IsPdbLoaded) { string pdb = module.TryDownloadPdb(null); if (pdb != null) { module.LoadPdb(pdb); } } var sourceLocation = module.GetSourceInformation(method, map.ILOffset); if (sourceLocation != null) { _context.WriteLine("{0} {1}-{2}:{3}-{4}", sourceLocation.FilePath, sourceLocation.LineNumber, sourceLocation.LineNumberEnd, sourceLocation.ColStart, sourceLocation.ColEnd); for (int line = sourceLocation.LineNumber; line <= sourceLocation.LineNumberEnd; ++line) { _context.WriteLine(ReadSourceLine(sourceLocation.FilePath, line)); _context.WriteLine(new string(' ', sourceLocation.ColStart - 1) + new string('^', sourceLocation.ColEnd - sourceLocation.ColStart)); } } PrintInstructions(instructions); DisassembleNative(map); } // TODO We are still not printing the epilogue while sosex does }
private void AddCompatibleOverloads(ClrMethod/*!*/ constructedMethod, ClrMethod/*!*/ genericMethod, DTypeDescs/*!*/ typeArgs, bool exactMatch) { foreach (ClrMethod.Overload overload in genericMethod.Overloads) { // add the overloads that are compatible with typeArgs int gen_count = overload.GenericParamCount; if (exactMatch ? gen_count == typeArgs.Count : gen_count > typeArgs.Count) { bool compatible = true; for (int i = 0; i < gen_count; i++) { DTypeDesc desc = (i < typeArgs.Count ? typeArgs[i] : PrimitiveTypeDesc.SystemObjectTypeDesc); if (!desc.IsCompatibleWithGenericParameter(overload.GenericParameters[i])) { compatible = false; break; } } if (compatible) { // make generic method Type[] real_type_args; if (exactMatch) real_type_args = typeArgs.GetRealTypes(); else { real_type_args = new Type[gen_count]; typeArgs.GetRealTypes(real_type_args, 0); for (int i = typeArgs.Count; i < gen_count; i++) real_type_args[i] = Types.Object[0]; } MethodInfo info = ((MethodInfo)overload.Method).MakeGenericMethod(real_type_args); ClrMethod.Overload constructed_overload; constructedMethod.AddOverload(info, out constructed_overload); } } } }
public DesktopStackFrame(DesktopRuntimeBase runtime, ulong sp, string method, ClrMethod innerMethod) { m_sp = sp; m_frameName = method; m_type = ClrStackFrameType.Runtime; m_method = innerMethod; m_runtime = runtime; }
internal static void ReflectFunction(DTypeDesc declaringType, MethodInfo real_function, Dictionary<string, DRoutineDesc> functions) { if (!real_function.IsSpecialName) { QualifiedName qualified_name = QualifiedName.FromClrNotation(real_function.Name, true); string full_name = qualified_name.ToString(); ClrMethod clr_function = null; DRoutineDesc existing; if (functions.TryGetValue(full_name, out existing)) { if (existing.DeclaringType.Equals(declaringType)) { Debug.Assert(existing is ClrMethodDesc, "CLR module should contain CLR methods only"); // an overload of existing CLR function: clr_function = existing.ClrMethod; } else { // ambiguous: clr_function = null; existing.MemberAttributes |= PhpMemberAttributes.Ambiguous; } } else { // new entry: clr_function = new ClrMethod(qualified_name.Name, declaringType, Enums.GetMemberAttributes(real_function), 1, real_function.ContainsGenericParameters); functions.Add(full_name, clr_function.ClrMethodDesc); } if (clr_function != null) { ClrMethod.Overload overload; clr_function.AddOverload(real_function, out overload); } } }
/// <summary> /// Creates a constructed method desc for the given type arguments. /// </summary> private ClrMethodDesc ConstructMethodDesc(DTypeDescs/*!*/ typeArgs) { ClrMethod generic_method = ClrMethod; ClrMethod constructed_method = new ClrMethod(generic_method.Name, declaringType, memberAttributes, 1, false); // add overloads which have an exact match AddCompatibleOverloads(constructed_method, generic_method, typeArgs, true); if (constructed_method.Overloads.Count == 0) { // now try overloads whose additional type parameters can by substituted by System.Object AddCompatibleOverloads(constructed_method, generic_method, typeArgs, false); if (constructed_method.Overloads.Count == 0) { // sorry, bad luck PhpException.NoSuitableOverload(generic_method.DeclaringType.FullName, generic_method.FullName); return null; } } return constructed_method.ClrMethodDesc; }
private static IEnumerable <Asm> Decode(ILToNativeMap map, State state, int depth, ClrMethod currentMethod) { ulong startAddress = map.StartAddress; uint size = (uint)(map.EndAddress - map.StartAddress); byte[] code = new byte[size]; int totalBytesRead = 0; do { int bytesRead = state.Runtime.DataTarget.DataReader.Read(startAddress + (ulong)totalBytesRead, new Span <byte>(code, totalBytesRead, (int)size - totalBytesRead)); if (bytesRead <= 0) { throw new EndOfStreamException($"Tried to read {size} bytes for {currentMethod.Signature}, got only {totalBytesRead}"); } totalBytesRead += bytesRead; } while (totalBytesRead != size); var reader = new ByteArrayCodeReader(code, 0, (int)size); var decoder = Decoder.Create(state.Runtime.DataTarget.DataReader.PointerSize * 8, reader); decoder.IP = startAddress; while (reader.CanReadByte) { decoder.Decode(out var instruction); TryTranslateAddressToName(instruction, state, depth, currentMethod); yield return(new Asm { InstructionPointer = instruction.IP, Instruction = instruction }); } }
public override SourceLocation GetSourceInformation(ClrMethod method, int ilOffset) { throw new NotImplementedException(); }
/// <summary> /// Code from http://stackoverflow.com/questions/2057781/is-there-a-way-to-get-the-stacktraces-for-all-threads-in-c-like-java-lang-thre/24315960#24315960 /// also see http://stackoverflow.com/questions/31633541/clrmd-throws-exception-when-creating-runtime/31745689#31745689 /// </summary> internal IList <OutputLine> PrintCodeForMethod(Benchmark benchmark, Process process, bool printAssembly, bool printIL, bool printDiagnostics) { this.process = process; logger.Clear(); //Method name format: "BenchmarkDotNet.Samples.Infra.RunFast()" (NOTE: WITHOUT the return type) var methodInfo = benchmark.Target.Method; fullTypeName = methodInfo.DeclaringType.FullName; var methodParams = string.Join(", ", methodInfo.GetParameters().Select(p => p.ParameterType.FullName)); fullMethodName = $"{fullTypeName}.{methodInfo.Name}({methodParams})"; logger?.WriteLine($"\nPrinting Code for Method: {fullMethodName}"); logger?.WriteLine($"\nPrintAssembly={printAssembly}, PrintIL={printIL}"); logger?.WriteLine($"Attaching to process {Path.GetFileName(process.MainModule.FileName)}, Pid={process.Id}"); logger?.WriteLine($"Path {process.MainModule.FileName}"); using (var dataTarget = DataTarget.AttachToProcess(process.Id, 5000, AttachFlag.NonInvasive)) { var runtime = SetupClrRuntime(dataTarget); if (printDiagnostics) { PrintRuntimeDiagnosticInfo(dataTarget, runtime); } if (printAssembly == false && printIL == false) { return(logger.CapturedOutput); } ClrType @class = runtime.GetHeap().GetTypeByName(fullTypeName); ClrMethod @method = @class.Methods.Single(m => m.GetFullSignature() == fullMethodName); DesktopModule module = (DesktopModule)@method.Type.Module; if (!module.IsPdbLoaded) { string pdbLocation = module.TryDownloadPdb(null); if (pdbLocation != null) { module.LoadPdb(pdbLocation); } } logger?.WriteLine($"Module: {Path.GetFileName(module.Name)}"); logger?.WriteLine($"Type: {method.Type.Name}"); logger?.WriteLine($"Method: {method.Name}"); // TODO work out why this returns locations inside OTHER methods, it's like it doesn't have an upper bound and just keeps going!? var ilOffsetLocations = module.GetSourceLocationsForMethod(@method.MetadataToken); string filePath = null; string[] lines = null; logger?.WriteLine(); for (int i = 0; i < ilOffsetLocations.Count; i++) { var location = ilOffsetLocations[i]; var ilMaps = @method.ILOffsetMap.Where(il => il.ILOffset == location.ILOffset).ToList(); if (ilMaps.Any() == false) { continue; } if (lines == null || location.SourceLocation.FilePath != filePath) { filePath = location.SourceLocation.FilePath; lines = File.ReadAllLines(filePath); logger?.WriteLine($"Parsing file {Path.GetFileName(location.SourceLocation.FilePath)}"); } PrintLocationAndILMapInfo(@method, location, ilMaps); PrintSourceCode(lines, location); if (printAssembly) { var debugControl = dataTarget.DebuggerInterface as IDebugControl; PrintAssemblyCode(@method, ilMaps, runtime, debugControl); } } return(logger.CapturedOutput); } }
public ClrOverloadBuilder(ILEmitter/*!*/ il, ClrMethod/*!*/ method, ConstructedType constructedType, IPlace/*!*/ stack, IPlace/*!*/ instance, bool emitParentCtorCall, ParameterLoader/*!*/ loadValueArg, ParameterLoader/*!*/ loadReferenceArg) { this.il = il; this.method = method; this.constructedType = constructedType; this.stack = stack; this.instance = instance; this.loadValueArg = loadValueArg; this.loadReferenceArg = loadReferenceArg; this.emitParentCtorCall = emitParentCtorCall; this.overloads = new List<Overload>(method.Overloads); SortOverloads(this.overloads); }
private string GenerateMethodInfo(ClrMethod method) { return($"{method.Type.Name}.{method.Name}"); }
public HackMethod(QHackContext context, ClrMethod method) { Context = context; InternalClrMethod = method; }