How to debug (step into) BinaryFormatter.Deseriali

2019-01-19 21:18发布

My app tries to deserialize data sent by client and it fails with the following error:

Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll

Additional information: Cannot get the member '<.ctor>b__0'.

googling gives no results. Okay, I decided I would step into deserialization logic and try to figure out what exactly is causing this. Well, a day has passed and I'm nowhere close.

I used instructions from Microsoft Reference Source website to configure Visual Studio. It does download something

MicrosoftPublicSymbols\mscorlib.pdb\
   DCF1E4D31F6944AC87E7A634262BEE881\mscorlib.pdb (780kb)
   E47257B512BA49BC9FC367C532FC5F1E2\mscorlib.pdb (953kb)

but debugger does not step in.

I googled more and found another way to do it - installed dotTrace app and used it as source server. And that does not help either. I still see the following:

enter image description here

Symbol Load Information popup for mscorlib.pdb says

C:\Users\me\AppData\Local\Temp\SymbolCache\MicrosoftPublicSymbols\mscorlib.pdb\e47257b512ba49bc9fc367c532fc5f1e2\mscorlib.pdb: Symbols loaded.

I can step in into System.Windows.Forms, System.Linq, etc - so generally speaking, it works - it just this particular call to BinaryFormatter.Deserialize() does not work. What could be the reasons for that and how can I get it to step into?

Could it be because of SecuritySafeCritical attribute?

[System.Security.SecuritySafeCritical] 
public Object Deserialize(Stream serializationStream)

I'm using VS 2015 .Net 4.5.2 (though I tried 4.5 with the same results).

3条回答
狗以群分
2楼-- · 2019-01-19 21:45

Microsoft doesn't upload the source of every mscorlib.dll update, this is why you only get public PDBs without any source data. But there is a Visual Studio Addon from Redgate's Reflector where you can decompile 3rd party DLLs and step through them in the VS debugger.

enter image description here

enter image description here

DotPeek from Jetbrains also supports PDB generation and hosting of a Symbol server to allow debugging.

Maybe this helps you to debug your issue.

查看更多
劫难
3楼-- · 2019-01-19 21:52

tl;dr: different version of a compiler (or different settings?) may generate different names for the generated method corresponding to an anonymous function. If such a method is pointed by a private field of a serialized class you get the exception, even though the source didn't change between the 2 builds.


I just tracked the exact type of situation, but with the deserialization triggered in an asp.net application's session. Like this case, the BinaryFormatter is used.

<.ctor>b__0 corresponds to the generated method corresponding to an anonymous function.

Now, the problem here is the dependency on such a method during serialization, because the name is not guaranteed to be the same over different builds (even with an unchanged source). This almost surely tracks to some kind of delegate in a private instance field of a serialized class. Note that such the class where the anonymous function is declared is not necessarily the class that holds the reference to that function in a private field.

Unfortunately I didn't have time to track why the same source produces different names for the anonymous function, but given the history of the project involved it is either a different compiler version of the options passed to it. I am convinced it is the earlier.

If you have access to the assemblies in both sides, you can confirm the change. At first I tried exporting the dissembled source of both assemblies in DotPeek and then doing a diff of the folders. That didn't prove a good process, but it may be due to some DotPeek settings that need to be set or something.

What worked better was using a combination of ndepend and reflector. You can do an assembly comparison of the earlier. The way I did it was to change one of the build in queries to get all constructors of serialized classes that had any kind of change. This narrowed it down to a few classes/constructors (there is a risk of not catching it this way, if the anonymous function was created in a non serializable class).

Once I had it down to a few constructors, from ndepend I opened an old vs new comparison that uses reflector for it. This not only showed the method name in the same format as the exception, but already showed got me to the right one in the code base.

Once I new the class, I found it better to open each assembly in a separate resharper window and see the methods of the class. It is quite visible there.

Also note that in the case where the code is changed, even the same compiler version/option may give you different names, so it is very brittle to have private fields in serializable classes that point to functions. The following answer expands on it: https://stackoverflow.com/a/1133465/66372

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-01-19 22:01

Without any details i can assume this is compatibility issue with versions of objects you trying to serialize & deserialize. Looks like client sends you some old object bits(without lambda in constructor). And your server running newer version of software searching for some lambda method.

<.ctor>b__0 - is method name for first lambda method in .ctor (object constructor).

So for example if you had on client's machine object A:

class A {
  public A() {
   int a = 5;
   int b = 7;
   // Plain code, no lambdas
  }
}

Then you updated your class on server introducing lambda in constructor:

class A {
  public A() {
   int a = 5;
   int b = 7;
   Func<int,int> some = x => x * 2 + a; 
  }
}

After that their binary representation is not the same, server version of A has private invisible method <.ctor>b__0 in it.

Generated IL for lamda method in class A1

查看更多
登录 后发表回答