Ars*_*ray 6 .net c# reflection stack-trace
我正在为我的c#应用程序开发一个免提日志机制.
这是我希望它看起来像:
函数a(arg1, arg2, arg 3.....)调用函数b(arg4,arg5,arg6....),它反过来调用log()哪些能够检测堆栈跟踪(这可以通过Environment.StackTrace),以及调用stacktrace中每个函数(例如a和b)的值.
我希望它在调试和发布模式下工作(或者至少在调试模式下).
这可以在.net中做吗?
Jon*_*nna 10
绝对不可能:
在b被调用的时候,一个人使用的堆栈中的空间arg1(IL堆栈,所以它可能从未被放入堆栈,但是已经在调用中注册)并不能保证仍被使用arg1.
通过扩展,如果arg1是引用类型,则如果在调用之后未使用它,则不保证它所引用的对象不被垃圾回收b.
编辑:
更详细一点,因为你的评论暗示你不是在讨论这个并且仍然认为它应该是可能的.
抖动使用的调用约定未在任何相关标准的规范中指定,这使得实现者可以自由地进行改进.它们确实在32位和64位版本以及不同版本之间有所不同.
但是,来自MS人员的文章表明,所使用的约定类似于__fastcall约定.在你的调用中a,arg1将被放入ECX寄存器*,并arg2进入EDX寄存器(我通过假设32位x86简化,其中amd64甚至更多参数被注册)运行代码的核心.arg3将被推入堆栈并确实存在于内存中.
需要注意的是,在这一点上,没有存储在哪个位置arg1和arg2存在,他们只是在一个CPU寄存器.
在执行方法本身的过程中,根据需要使用寄存器和存储器.而b被调用.
现在,如果a需要,arg1或者arg2它必须在它调用之前推动它b.但如果没有,那么它就不会 - 甚至可能会重新订购以减少这种需求.相反,这些寄存器可能已经被用于其他东西 - 抖动并不是愚蠢的,所以如果它需要一个寄存器或堆栈上的一个插槽,并且有一个未使用该方法的其余部分,它就会发生重用那个空间.(就此而言,在上面的层次上,C#编译器将重用IL生成的虚拟堆栈中的槽使用).
因此,当b被调用时,arg4将其放入寄存器ECX,arg5进入EDX并arg6推入堆栈.在这一点上,arg1与arg2 不存在,你不能再找出他们比你可以读一本书,它已被回收,变成卫生纸了.
(有趣的是,一个方法在同一个位置调用另一个具有相同参数的方法是很常见的,在这种情况下,ECX和EDX可以保持不变).
然后,b返回,将其返回值放入EAX寄存器,或EDX:EAX对或内存中,EAX指向它,具体取决于大小,a在将其返回到该寄存器之前做更多工作,依此类推.
现在,假设没有做任何优化.实际上,它可能b根本没有被调用,而是它的代码被内联.在这种情况下,寄存器中或堆栈中的值 - 以及后一种情况下它们在堆栈中的位置 - 是否与b签名无关,以及与相关值在何处相关a的所有内容执行,这将是在另一种情况下"呼"地不同的b,甚至在其他情况下"呼"地b从a,因为整个呼叫a包括其呼叫b可能已经在一种情况下内联,在没有内联另一个,并在另一个内联不同.例如,如果arg4直接来自另一个调用返回的值,那么此时它可能在EAX寄存器中,而arg5在ECX中则与它相同,arg1并且arg6位于堆栈空间中间的某个位置用于a.
另一种可能性是调用b是一个被消除的尾调用:因为调用b将立即返回其返回值a(或其他一些可能性),而不是推送到堆栈,使用的值by a被替换为原位,并且返回地址已更改,以便返回从b跳转回调用的方法a,跳过一些工作(并减少内存使用,以至于某些功能样式方法会溢出堆栈而不是工作确实工作得很好.在这种情况下,在调用期间b,参数a可能完全消失,即使是那些已经在堆栈中的参数.
最后的案例是否应该被视为优化,这是值得商榷的.有些语言在很大程度上依赖于它的完成,因为它们具有良好的性能,如果它们甚至可以工作而不会产生可怕的性能(而不是溢出堆栈).
可以有各种其他优化方式.这里应该有其他的优化的各种方式-如果.NET团队或Mono团队做一些让我的代码更快或使用较少的内存,但否则相同的行为,而不必我的东西,我一个也不会抱怨!
而且假设首先编写C#的人从未改变过参数的值,这当然不是真的.考虑以下代码:
IEnumerable<T> RepeatedlyInvoke(Func<T> factory, int count)
{
if(count < 0)
throw new ArgumentOutOfRangeException();
while(count-- != 0)
yield return factory();
}
Run Code Online (Sandbox Code Playgroud)
即使C#编译器和抖动是以如此浪费的方式设计的,你可以保证参数不会以上述方式改变,你怎么能知道count调用中的内容是factory什么?即使在第一次调用时它也是不同的,并且它不像以上那样是奇怪的代码.
所以,总结一下:
从这一切来看,究竟怎么可能知道是什么arg1?
现在,添加垃圾收集的存在.想象一下,如果我们能够神奇地知道究竟arg1是什么,尽管如此.如果它是对堆上对象的引用,它可能仍然对我们没有好处,因为如果以上所有意味着堆栈上没有更多的引用活动 - 并且应该很清楚,这肯定会发生 - 并且GC启动,然后可以收集对象.因此,我们可以神奇地掌握的是对不再存在的东西的引用 - 实际上很可能是堆中的某个区域现在被用于其他东西,爆炸使整个框架的整个类型安全!
与获得IL的反射相比,它没有任何可比性,因为:
MethodBody该方法,那么它通常内联的事实是无关紧要的.关于性能分析,AOP和拦截的其他答案中的建议尽可能接近.
*实际上,它this是实例成员的真正第一个参数.让我们假装一切都是静态的,所以我们不必一直指出这一点.
| 归档时间: |
|
| 查看次数: |
317 次 |
| 最近记录: |