前言

本文介绍一种使用IL的方式直接跟踪exception到行的方法,让大家对exception不再感到恶心!特别是

System.NullReferenceException: 未将对象引用设置到对象的实例。

问题的导火线

今天在debug的时候,又出现了空指针,我这次真的火了!每次遇到空指针,.net给出的信息总是非常的少,我根本不知道是哪里Throw出来的,只能反复检查代码。

我火了!我要起义!于是,开始寻求一种能够出现exception后知道什么代码报出来的。例如以下代码:

代码

     class  Program
    {
         static  void  Main( string [] args)
        {
             try
            {
                 string  hello3 = null ;
                hello3  =  hello3.ToUpper();
            }
             catch  (Exception ex)
            {
                Console.Write(ex.ToString());
            }

}
    }

在release模式下,没有pdb的时候,微软给出的答案是:

System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 Pixysoft.Testdriven.Consoles.Program.Main(String[] args)

我靠!鬼才知道哪里出现了空指针?我们的程序比这个复杂多了,在层层代码中、上百行的方法体内,神才知道空指针是什么地方报的。

于是我开始思考如何能够知道程序运行到什么地方出现异常。。

1. 首先是想到了aop,对方法体拦截。但是还是不能知道方法内部。

2. 然后想到了反射,问题还是同上。

3. 然后想到了StackTrace trace = new StackTrace(exception, true); 直接获取调用堆栈Frame。可是在没有pdb的时候,frame.GetFileLineNumber() 返回的是0. 傻逼了。

4. 然后想到了一个软件NCover。他就能够知道代码运行到什么阶段。于是开始查Ncover的源码。。不查不知道,一查吓一跳,原来NCover是对我们原方法进行重构,入侵了自己的计数器(c# code),然后动态编译出来的。郁闷。看来NCover要放弃了。

4. 最后,我会想起了曾经做过的IL。终于。。。看到了一丝希望:

int offset = frame.GetILOffset();

在没有pdb的时候,IL的偏移量仍然正常输出。

正文

思路大概是:

1. 获取exception的调用堆栈。

2. 获取exception相关的这个方法的方法的IL代码

3. 结合excpetion的IL偏移量和方法的IL,把调用源找出来。

代码如下:

代码

     class  Program
    {
         static  void  Main( string [] args)
        {
             try
            {
                 string  hello3  =  null ;

hello3  =  hello3.ToUpper();
            }
             catch  (Exception ex)
            {
                 // 获取调用堆栈

StackTrace trace  =  new  StackTrace(ex,  true );

StackFrame frame  =  trace.GetFrame( 0 );

int  offset  =  frame.GetILOffset();

byte [] il  =  frame.GetMethod().GetMethodBody().GetILAsByteArray();

// 获取调用指令

offset ++ ;

ushort  instruction  =  il[offset ++ ];

// 打开潘多拉魔盒

ILGlobals  global  =  new  ILGlobals();

global .LoadOpCodes();

// 翻译

OpCode code  =  OpCodes.Nop;

if  (instruction  !=  0xfe )
                {
                    code  =  global .SingleByteOpCodes[( int )instruction];
                }
                 else
                {
                    instruction  =  il[offset ++ ];
                    code  =  global .MultiByteOpCodes[( int )instruction];
                    instruction  =  ( ushort )(instruction  |  0xfe00 );
                }

// 获取方法信息

int  metadataToken  =  ReadInt32(il,  ref  offset);

MethodBase callmethod  =  frame.GetMethod().Module.ResolveMethod(metadataToken,
                     frame.GetMethod().DeclaringType.GetGenericArguments(),
                     frame.GetMethod().GetGenericArguments());

// 完成

Console.WriteLine(callmethod.DeclaringType  +  " . "  +  callmethod.Name);

Console.Read();
            }

}

private  static  int  ReadInt32( byte [] il,  ref  int  position)
        {
             return  (((il[position ++ ]  |  (il[position ++ ]  <<  8 ))  |  (il[position ++ ]  <<  0x10 ))  |  (il[position ++ ]  <<  0x18 ));
        }

}

public  class  ILGlobals
    {
         private  OpCode[] multiByteOpCodes;

private  OpCode[] singleByteOpCodes;

///  <summary>
         ///  Loads the OpCodes for later use.
         ///  </summary>
         public  void  LoadOpCodes()
        {
            singleByteOpCodes  =  new  OpCode[ 0x100 ];
            multiByteOpCodes  =  new  OpCode[ 0x100 ];
            FieldInfo[] infoArray1  =  typeof (OpCodes).GetFields();
             for  ( int  num1  =  0 ; num1  <  infoArray1.Length; num1 ++ )
            {
                FieldInfo info1  =  infoArray1[num1];
                 if  (info1.FieldType  ==  typeof (OpCode))
                {
                    OpCode code1  =  (OpCode)info1.GetValue( null );
                     ushort  num2  =  ( ushort )code1.Value;
                     if  (num2  <  0x100 )
                    {
                        singleByteOpCodes[( int )num2]  =  code1;
                    }
                     else
                    {
                         if  ((num2  &  0xff00 )  !=  0xfe00 )
                        {
                             throw  new  Exception( " Invalid OpCode. " );
                        }
                        multiByteOpCodes[num2  &  0xff ]  =  code1;
                    }
                }
            }
        }

///  <summary>
         ///  Retrieve the friendly name of a type
         ///  </summary>
         ///  <param name="typeName">
         ///  The complete name to the type
         ///  </param>
         ///  <returns>
         ///  The simplified name of the type (i.e. "int" instead f System.Int32)
         ///  </returns>
         public  static  string  ProcessSpecialTypes( string  typeName)
        {
             string  result  =  typeName;
             switch  (typeName)
            {
                 case  " System.string " :
                 case  " System.String " :
                 case  " String " :
                    result  =  " string " ;  break ;
                 case  " System.Int32 " :
                 case  " Int " :
                 case  " Int32 " :
                    result  =  " int " ;  break ;
            }
             return  result;
        }

public  OpCode[] MultiByteOpCodes
        {
             get  {  return  multiByteOpCodes; }
        }

public  OpCode[] SingleByteOpCodes
        {
             get  {  return  singleByteOpCodes; }
        }
    }

这样,输出的结果是:

System.String.ToUpper

在这里出现了空指针。

后续

看到这里,大家应该明白我为啥大骂微软了。

明明所有的信息都能够提供,都在IL里面,但是这个该死的微软就是不提供。给个exception还这么暧昧,让我们不断的浪费时间去debug。

实际上,微软的.net framework完全掌握了我们每一行代码的运行情况,内存情况。怪不得现在出了个VS2010,搞了个什么Intellitrace,所谓历史调试什么的。

希望通过这篇文章,唤醒大家,其实我们可以走的更远!

技术支持

zc22.cnblogs.com

reborn_zhang@hotmail.com

转载于:https://www.cnblogs.com/anjing/archive/2009/12/25/1632016.html

转:使用IL的方式直接跟踪exception到行的方法相关推荐

  1. 微软真是个十足的混蛋啊!让我们跟踪Exception到行把!(不明真相群众请入)...

    前言 本文介绍一种使用IL的方式直接跟踪exception到行的方法,让大家对exception不再感到恶心!特别是 System.NullReferenceException: 未将对象引用设置到对 ...

  2. python PyQt5 Signal类 (Signal类提供了一种以pythonic方式声明和连接Qt信号的方法)(connect()、disconnect()、emit())

    https://doc.qt.io/qtforpython/PySide2/QtCore/Signal.html?highlight=connect#PySide2.QtCore.Signal.con ...

  3. java post 提交数据_使用Post方式提交数据到Tomcat服务器的方法

    我在上一篇文章中介绍了 使用Get方式提交数据到Tomcat服务器,这篇将介绍使用Post方式提交数据到服务器,由于Post的方式和Get方式创建Web工程是一模一样的,只用几个地方的代码不同所以,我 ...

  4. 中断方式下进行串口通讯的正确方法

    转载:http://bbs.ednchina.com/BLOG_ARTICLE_277752.HTM 中断方式下进行串口通讯的正确方法 一般普遍的把串口通讯分为查询方式和中断方式.查询方式比较容易理解 ...

  5. python发送文件到服务器_python 使用poster模块进行http方式的文件传输到服务器的方法...

    这几天帮内部人员做一个文件传输的小工具,要用http的方式,在用django搭建了个小框架之后,如何进行传输,特别是大文件的传输,成为主要问题.经过查资料,最后选择了通过poster这个模块来进行文件 ...

  6. 串口服务器工作方式及常见异常故障问题排除方法介绍

    串口设备联网服务器就像一台带CPU.实时操作系统和TCP/IP协议的微型电脑,方便在串口和网络设备中传输数据.您可以在世界任何位置通过网络,用您的计算机来存取,管理和配置远程的设备.但是我们在实际使用 ...

  7. 点击网页跟踪php代码的工具,使用ltrace工具跟踪PHP库函数调用的方法

    本文实例讲述了使用ltrace工具跟踪PHP库函数调用的方法.分享给大家供大家参考,具体如下: 可能大家已经很熟悉使用strace来跟踪系统调用,今天介绍一个跟踪库函数的利器ltrace 比如我有这么 ...

  8. python文件传输模块_[宜配屋]听图阁 - python 使用poster模块进行http方式的文件传输到服务器的方法...

    这几天帮内部人员做一个文件传输的小工具,要用http的方式,在用django搭建了个小框架之后,如何进行传输,特别是大文件的传输,成为主要问题.经过查资料,最后选择了通过poster这个模块来进行文件 ...

  9. python调用http方法_python 使用poster模块进行http方式的文件传输到服务器的方法

    这几天帮内部人员做一个文件传输的小工具,要用http的方式,在用django搭建了个小框架之后,如何进行传输,特别是大文件的传输,成为主要问题.经过查资料,最后选择了通过poster这个模块来进行文件 ...

最新文章

  1. protobuf编码
  2. 15年考的全国计算机应用技术,(2015年全国专业技术人员计算机应用能力考试.doc...
  3. 『数据库』朴实无华的数据库多表查询,连接查询、笛卡尔积
  4. k8s pod MySQL环境变量_Kubernetes 配置Pod和容器(一)定义容器环境变量
  5. C++与Java中的static成员总结
  6. MongoDB-与SpringBoot集成
  7. 《机器视觉算法与应用》第3章 机器视觉算法之光学字符识别(OCR)——学习笔记
  8. 网站/APP 流量分析、用户访问分析
  9. window新建文本快捷键
  10. 用js转换joson返回数据库的时间格式为/Date(*************)/
  11. 移动端统计分析工具Firebase、AppsFlyer、Adjust、Flurry、Tap stream、Kochava 、branch不完全对比分析
  12. php00截断原理,burpsuite上传截断及截断原理介绍
  13. vue项目性能优化(图片优化)
  14. Job for tomcat.service failed because the control process exited with error code 解决办法:
  15. 跨境电商必知的交叉销售和追加销售:2022终极指南
  16. 天翼云服务器挂载硬盘
  17. python自动填写腾讯文档_腾讯文档自动填充工具(工具),填写
  18. Kubernetes(k8s)集群安装(需要3台centos7)
  19. python电影推荐算法_基于Python的电影推荐算法
  20. python读取docx文件_Python读写docx文件的方法

热门文章

  1. 安装nginx—并创建域名主机
  2. 萌新SQL基础学习1
  3. tomcat启动失败的3种解决办法
  4. Spring Cloud Gateway网关实现短网址生成、解析、转发
  5. 第九周coreidraw总结
  6. Java_JavaBean映射框架Orika
  7. golang单元测试一(简单函数测试)
  8. 微机原理-80386(3)
  9. 【译Py】数据科学面试终极指南(四)
  10. F - Friends