惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
SegmentFault 最新的问题
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
P
Proofpoint News Feed
Last Week in AI
Last Week in AI
U
Unit 42
W
WeLiveSecurity
博客园 - Franky
Recent Announcements
Recent Announcements
Hacker News - Newest:
Hacker News - Newest: "LLM"
Attack and Defense Labs
Attack and Defense Labs
月光博客
月光博客
The Cloudflare Blog
Spread Privacy
Spread Privacy
腾讯CDC
P
Privacy International News Feed
N
News and Events Feed by Topic
AWS News Blog
AWS News Blog
NISL@THU
NISL@THU
T
Troy Hunt's Blog
小众软件
小众软件
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Microsoft Security Blog
Microsoft Security Blog
L
Lohrmann on Cybersecurity
Webroot Blog
Webroot Blog
Y
Y Combinator Blog
量子位
P
Palo Alto Networks Blog
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
K
Kaspersky official blog
IT之家
IT之家
T
Threat Research - Cisco Blogs
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
C
Check Point Blog
Blog — PlanetScale
Blog — PlanetScale
爱范儿
爱范儿
G
Google Developers Blog
S
Secure Thoughts

博客园 - 笑清风

IDEA 2025.2 Maven 在windows下编译打包中文乱码解决方案 milvus相关概念 milvus介绍 Mockito之doThrow 关于行为面试法——STAR JVM 内存分析工具 MAT 的深度讲解与实践——入门篇《转》 linux top 中的time+ notepad++删除包含指定字符串的行(正则) JVM指令码表 Hadoop家族成员概述 轻松把玩HttpClient之封装HttpClient工具类 Base64 SOAP1.1&SOAP1.2 画图解释SQL联合语句 Elasticsearch相关资源 SQL Server 数据类型映射 SQL Server类型的对应关系 The CLR's Thread Pool .Net下获取程序路径的方法
Rethrowing exceptions and preserving the full call stack trace
笑清风 · 2013-09-11 · via 博客园 - 笑清风

refer:http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx

http://geekswithblogs.net/sdorman/archive/2007/08/20/Difference-between-quotthrowquot-and-quotthrow-exquot-in-.NET.aspx

Did you know that depending on the way you rethrow exceptions you may lose important information? There are already several blog posts that explain and demonstrate the difference between throw and throw ex. I'm realizing only now that none of the two solutions yields the complete call stack trace information!

Let's see what the problem is and I'll show you the real solution.

I'll use the following method to generate an exception:

private static void BadWork()
{
  int i = 0;
  int j = 12 / i; // Line 10: DivideByZeroException
  int k = j + 1;
}

Let's consider what happens if we call BadWork and rethrow the exception with throw ex as follows:

try
{
  BadWork();
}
catch (Exception ex)
{
  // do something
  // ...
  throw ex; // Line 24
}

Here is the call stack trace that we get in this case:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowEx() in Program.cs:line 24
   at Program.Main(String[] args) in Program.cs:line 88

Line 24 is where throw ex is, not where the exception was thrown.

Let's now replace throw ex by throw:

try
{
  BadWork();
}
catch
{
  // do something
  // ...
  throw; // Line 38
}

This time, here is the call stack trace:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.BadWork() in Program.cs:line 10
   at Program.WithThrow() in Program.cs:line 38
   at Program.Main(String[] args) in Program.cs:line 89

As you can see, we get one additional stack frame this time. Line 10 is where the exception was thrown, which is important information because this is the only information that identifies where the exception actually happened.

This shows that it's better to use throw rather than throw ex if you want the full stack trace information to be preserved. However, there are cases where throw is not enough. In the following example, throw does not preserve the full stack trace:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

This time, you can see that information is lost again. Line 54 is where throw is, not where the exception was thrown.

To preserve the full call stack information, you need to use the following method:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

This method can be used as follows: 

try
{
  int i = 0;
  int j = 12 / i; // Line 78
  int k = j + 1;
}
catch (Exception ex)
{
  // do something
  // ...
  PreserveStackTrace(ex);
  throw; // Line 86
}

Here is the new call stack information you get with the above code:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowAndStackTracePreservation() in Program.cs:line 78
   at Program.WithThrowAndStackTracePreservation() in Program.cs:line 86
   at Program.Main(String[] args) in Program.cs:line 110

Here is the call stack information you get with throw ex and a call to PreserveStackTrace:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.BadWork() in Program.cs:line 10
   at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 62
   at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 69
   at Program.Main(String[] args) in Program.cs:line 109

Here we get the full call stack information. Lines 78 and 10 are where the exceptions were thrown. To my knowledge, this is the only way to get complete call stack information in your logs. Without it, it may be difficult to hunt down some bugs.
It's worth noting that if you call PreserveStackTrace, then you can use throw or throw ex and you'll equally get the full stack trace information.

I found this useful trick on Chris Taylor's blog. If you want to use this with .NET 1, you should refer to Chris' post because it seems that the InternalPreserveStackTrace method didn't exist before .NET 2.0.

The complete source code is attached to this post.