星期四, 九月 27, 2007

Non-virtual calls to virtual methods



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by abhinaba on 9/27/07

C# compiler is known to emit virtual calls for non-virtual methods (callvirt instead of call IL instruction, maybe that be the topic of my next post). However, sometimes it's forced to do the exact opposite

Consider the following code

    class B     {         public virtual void Foo()         {             Console.WriteLine("Base::Foo");         }     }      class D : B     {         public override void Foo()         {             base.Foo();             this.Foo(); // this is infinite recursion. Put just for example             Console.WriteLine("Derived::Foo");         }     }

Here B::Foo is a virtual method and hence should be called virtually for both the calls in D::Foo. However, that's not the case.

For the above code the emitted IL looks like

    L_0001: ldarg.0      L_0002: call instance void BaseCall.B::Foo()     L_0007: nop      L_0008: ldarg.0      L_0009: callvirt instance void BaseCall.B::Foo()

So for base.Foo() call the non-virtual call instruction is generated and for the this.Foo() call the virtual callvirt instruction is generated.

The reason is obviously simple. If a virtual call was made on base.Foo() then the call would've landed in the derived D::Foo which would again call base.Foo() resulting in infinite recursion.

Any guess on what happens for the following code

    class B     {         public virtual void Foo()         {             Console.WriteLine("Base::Foo");         }     }      class C : B     {         public override void Foo()         {             Console.WriteLine("C::Foo");         }     }      class D : C     {         public override void Foo()         {             base.Foo();             Console.WriteLine("Derived::Foo");         }
}

 
 

Things you can do from here:

 
 

星期三, 九月 26, 2007

The Trouble with Threat Modeling



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by sdl on 9/26/07

Adam Shostack here.

I said recently that I wanted to talk more about what I do. The core of what I do is help Microsoft's product teams analyze the security of their designs by threat modeling. So I'm very concerned about how well we threat model, and how to help folks I work with do it better. I'd like to start that by talking about some of the things that make the design analysis process difficult, then what we've done to address those things. As each team starts a new product cycle, they have to decide how much time to spend on the tasks that are involved in security. There's competition for the time and attention of various people within a product team. Human nature is that if a process is easy or rewarding, people will spend time on it. If it's not, they'll do as little of it as they can get away with. So the process evolves, because, unlike Dr No, we want to be aligned with what our product groups and customers want

There have been a lot of variants of things called "threat modeling processes" at Microsoft, and a lot more in the wide world. People sometimes want to argue because they think Microsoft uses the term "threat modeling" differently than the rest of the world. This is only a little accurate. There is a community which uses questions like "what's your threat model" to mean "which attackers are you trying to stop?" Microsoft uses threat model to mean "which attacks are you trying to stop?" There are other communities whose use is more like ours. In this paragraph, I'm attempting to mitigate a denial of service threat, where prescriptivists try to drag us into a long discussion of how we're using words.) The processes I'm critiquing here are the versions of threat modeling that are presented in Writing Secure Code, Threat Modeling, and The Security Development Lifecycle books.

In this first post of a series on threat modeling, I'm going to talk a lot about problems we had in the past. In the next posts, I'll talk about what the process looks like today, and why we've made the changes we've made. I want to be really clear that I'm not critiquing the people who have been threat modeling, or their work. A lot of people have put a tremendous amount of work in, and gotten some good results. There are all sorts of issues that our customers will never experience because of that work. I am critiquing the processes, saying we can do better, in places we are doing better, and I intend to ensure we continue to do better.

We ask feature teams to participate in threat modeling, rather than having a central team of security experts develop threat models. There's a large trade-off associated with this choice. The benefit is that everyone thinks about security early. The cost is that we have to be very prescriptive in how we advise people to approach the problem. Some people are great at "think like an attacker," but others have trouble. Even for the people who are good at it, putting a process in place is great for coverage, assurance and reproducibility. But the experts don't expose the cracks in a process in the same way as asking everyone to participate.

Getting Started

The first problem with 'the threat modeling process' is that there are a lot of processes. People, eager to threat model, had a number of TM processes to choose from, which led to confusion. If you're a security expert, you might be able to select the right process. If you're not, judging and analyzing the processes might be a lot like analyzing cancer treatments. Drugs? Radiation? Surgery? It's scary, complex, and the wrong choice might lead to a lot of unnecessary pain. You want expert advice, and you want the experts to agree.

Most of the threat modeling processes previously taught at Microsoft were long and complex, having as many as 11 steps. That's a lot of steps to remember. There are steps which are much easier if you're an expert who understands the process. For example, 'asset enumeration.' Let's say you're threat modeling the GDI graphics library. What are the assets that GDI owns? A security expert might be able to answer the question, but anyone else will come to a screeching halt, and be unable to judge if they can skip this step and come back to it. (I'll come back to the effects of this in a later post.)

I wasn't around when the processes were created, and I don't think there's a lot of value in digging deeply into precisely how it got where it is. I believe the core issue is that people tried to bring proven techniques to a large audience, and didn't catch some of the problems as the audience changed from experts to novices.

The final problem people ran into as they tried to get started was an overload of jargon, and terms imported from security. We toss around terms like repudiation as if everyone should know what it means, and sometimes implied they're stupid if they don't. (Repudiation is claiming that you didn't do something. For example, "I didn't write that email!," "I don't know what got into me last night!" You can repudiate something you really did, and you can repudiate something you didn't do.) Using jargon sent several unfortunate messages:

  1. This is a process for experts only.
  2. You're not an expert.
  3. You can tune out now.
  4. We don't really expect you to do this well.

Of course, that wasn't the intent, but it often was the effect.

The Disconnected Process

Another set of problems is that threat modeling can feel disconnected from the development process. The extreme programming folks are fond of only doing what they need to do to ship, and Microsoft shipped code without threat models for a long time. The further something is from the process of building code, the less likely it is to be complete and up to date. That problem was made worse because there weren't a lot of people who would say "let me see the threat model for that." So there wasn't a lot of pressure to keep threat models up to date, even if teams had done a good job up front with them. There may be more pressure with other specs which are used by a broader set of people during development.

Validation

Once a team had started threat modeling, they had trouble knowing if they were doing a good job. Had they done enough? Was their threat model a good representation of the work they had done, or were planning to do? When we asked people to draw diagrams, we didn't tell them when they could stop, or what details didn't matter. When we asked them to brainstorm about threats, we didn't guide them as to how many they should find. When they found threats, what were they supposed to do about them? This was easier when there was an expert in the room to provide advice on how to mitigate the threat effectively. How should they track them? Threats aren't quite bugs--you can never remove a threat, only mitigate it. So perhaps it didn't make sense to track them like that, but that left threats in a limbo.

"Return on Investment"

The 11 step threat modeling processes were not only challenging, they were expensive. The time invested often didn't seem like it was paying off. Sometimes it really didn't pay off. (David LeBlanc makes this point forcefully in "Threat Modeling the Bold Button is Boring") Sometimes it just felt that way--Larry Osterman made that point, unintentionally in "Threat Modeling Again, Presenting the PlaySound Threat Model," where he said "Let's look at a slightly more interesting case where threat modeling exposes an issue." Youch! But as I wrote in a comment on that post, "What you've been doing here is walking through a lot of possibilities. Some of those turn out to be uninteresting, and we learn something. Others (as we've discussed in email) were pretty clearly uninteresting" It can be important to walk through those possibilities so we know they're uninteresting. Of course, we'd like to reduce the time it takes to look at each uninteresting issue.

Other Problems

Larry Osterman lays out some other reasons threat modeling is hard in a blog post: http://blogs.msdn.com/larryosterman/archive/2007/08/30/threat-modeling-once-again.asp

One thing that was realized very early on is that our early efforts at threat modeling were quite ad-hoc. We sat in a room and said "Hmm, what might the bad guys do to attack our product?" It turns out that this isn't actually a BAD way of going about threat modeling, and if that's all you do, you're way better off than you were if you'd done nothing.

Why doesn't it work? There are a couple of reasons:

It takes a special mindset to think like a bad guy. Not everyone can switch into that mindset. For instance, I can't think of the number of times I had to tell developers on my team "It doesn't matter that you've checked the value on the client, you still need to check it on the server because the client that's talking to your server might not be your code.".

Developers tend to think in terms of what a customer needs. But many times, the things that make things really cool for a customer provide a superhighway for the bad guy to attack your code.

It's ad-hoc. Microsoft asks every single developer and program manager to threat model (because they're the ones who know what the code is doing). Unfortunately that means that they're not experts on threat modeling. Providing structure helps avoid mistakes.

With all these problems, we still threat model, because it pays dividends. In the next posts, I'll talk about what we've done to improve things, what the process looks like now, and perhaps a bit about what it might look like either in the future, or adopted by other organizations.


 
 

Things you can do from here:

 
 

星期二, 九月 25, 2007

Unions in C#



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by abhinaba on 9/24/07

Usage of unions in the native world is pretty common. However, the same is not true for the .NET world. However, while using interop sometimes you need to fiddle around this these.

In C# you go about defining unions using the Explicit layout supported by struct as follows.

[StructLayout(LayoutKind.Explicit)] public struct MyUnion {     [FieldOffset(0)]     public UInt16 myInt;      [FieldOffset(0)]     public Byte byte1;      [FieldOffset(1)]     public Byte byte2; }

Here the StructLayout(LayoutKind.Explicit) is used to indicate that the stuct definition contains the layout explicitely. FieldOffset(offset) is used to specify the offset of the struct field from the start of the struct.

In the example above the layout of the struct is somewhat as follows

<<========== MyUnion (16 bits)========>> +--------------------------------------+ |             myInt (16-bit)           | +------------------+-------------------+ |  byte1 (8 bit)   |   byte2 (8 bit)   | +------------------+-------------------+

byte1 and byte2 share storage with myInt. The following code prints them out and highlight the fact that the system I used (Intel processor based) is little-endian.

MyUnion union; union.byte1 = 0; // needed to make the compiler happy union.byte2 = 0; union.myInt = 0xAABB; Console.WriteLine("{0:X}", union.byte1); Console.WriteLine("{0:X}", union.byte2);  // output is BB AA

Since the system is little-endian the LSB (0xBB) goes to the first byte and the MSB (0xAA)goes to the second.

<rant>

All this is very cool. The only thing that made me a bit unhappy is that the definite assignment verifier couldn't figure out that I needn't do the byte1, byte2 assignment to access them.

</rant>


 
 

Things you can do from here:

 
 

星期一, 九月 24, 2007

Saving Resume Information, part II



 
 

Hudong 通过 Google 阅读器发送给您的内容:

 
 

于 07-9-24 通过 MSDN Blogs 作者:Amy

In a previous post, I stated that when you press the stop button on your player, you have 2 seconds to continue to execute code before the player shuts down. But, that statement needs to be qualified. If you try to delay the application_end (or the deprecated stop_request) event by calling preventDefault when the user has pressed the stop, eject, or power button, the player will stop permitting preventDefault after two seconds. If you do not call preventDefault, you have a single tick to execute your code. This means that the function that handles the application_end event will be called, but not any callbacks that would have been handled in subsequent ticks. This is why using setContentInformation works, but trying to write your own XML file will not execute to completion.

However, if you just add a call to preventDefault to the event handler, and the application_end is actually triggered by the title end (and not the user pressing stop), you will end up creating an infitite loop. So, if you want to use preventDefault to allow more execution time, make sure to include an exit point in your script so that the application can end when your script is complete.

Below is an example of how the previous project would be modified if you were to call preventDefault on the application_end event:

addEventListener("application_end", handleApplicationEnd, false);

/******************************
BEGIN ADDED CODE
********************************/
//end sequence status
var endSeq = {
initialized:false,
complete:false
};
/******************************
END ADDED CODE
********************************/

function setResume( time )
{
try
{
//set key for resume time
psd.setContentInformation(contentID, resumeKey, time, setContentInfoCB);
}
catch(ex)
{
/******************************
BEGIN ADDED CODE
********************************/
//set end sequence to complete
endSeq.complete = true;
/******************************
END ADDED CODE
********************************/
TraceError("EXCEPTION on setContentInformation", ex, arguments.callee);
}

function setContentInfoCB( result, key )
{
if ( result == PersistentStorageManager.SUCCEEDED )
{
TraceInfo("SUCCEEDED on setContentInformation", arguments.callee);
}
else
{
TraceInfo("FAILED on setContentInformation", arguments.callee);
}
/******************************
BEGIN ADDED CODE
********************************/
//set end sequence to complete
endSeq.complete = true;
/******************************
END ADDED CODE
********************************/
}
}

function handleApplicationEnd(evt)
{

/******************************
BEGIN ADDED CODE
********************************/
//end sequence complete, exit
if (endSeq.complete) return;

//prevent end for another tick
evt.preventDefault();

//end sequence initialized, exit
if (endSeq.initialized) return;

endSeq.initialized = true;
/******************************
END ADDED CODE
********************************/

//event should pass value time based on title timeline
//if not, use elapsedTime
//this value could be equal to or greater than the movie runtime,
//check for this before using value in resume
var time = evt.time && (evt.time).match(timeCodeRegEx) ? evt.time : Player.playlist.currentTitle.elapsedTime;
setResume(time);
}


 
 

可从此处完成的操作:

 
 

Get the Sprint Mogul for $275 plus $25 in free accessories



 
 

Sent to you by Hudong via Google Reader:

 
 

 
 

Things you can do from here:

 
 
中印涉案金额最大反倾销案结束印方撤诉_新闻中心_新浪网


中印涉案金额最大反倾销案结束印方撤诉
http://www.sina.com.cn 2007年09月25日02:57 京华时报

  本报讯 (记者胡笑红)中国纺织品进出口商会副会长兼秘书长王宇昨天证实,印度中央丝绸局目前已撤诉。中印涉案金额最大的反倾销案终于结束。

  2005年5月,印方对原产于中国的绸缎进行反倾销调查。2006年11月,印度商工部反倾销总局发布终裁结果。相对于初裁,终裁对中国丝绸设定的最低限价降低了约30%。
javascript:void(0)
Publish
  印度中央丝绸局不满此终裁,将中国纺织品进出口商会、中国30多家应诉企业以及作出终裁的印度财政部和印度商工部一并告上印度法院。

  王宇说,目前尚不清楚印方撤诉的原因,商会正在了解。
Creating a Splash Screen Form on a Separate Thread

Classes in Jscript - Part I



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by don.raman on 9/24/07

I am Ritesh Parikh and have recently moved to JScript team as a SDET. While starting on JScript I had a lot of questions. One of them was - Is JScript object oriented?

Yes, JScript is object oriented. All object oriented languages allow us to define a Class and then create individual objects that are instances of that Class. So does JScript allow us to define Classes and create instances?

Yes it does, but it does not have a formal notion of a Class. As a result it is quite different from classic object-oriented languages like C++ or Java. JScript does not support Class based inheritance, instead uses prototype based inheritance.

If JScript does not have a formal notion of a Class then how does it support defining Classes and creating instances. This is what I am going to explain in this blog!!

JScript does not support true Classes the way most traditional languages do. However it is possible to simulate Classes in JScript using constructor functions and prototype objects.

Let's first define a few terms that we will use through this post:

Class: (As in strongly typed languages) defines the structure of an object. It defines exactly what fields an object contains, their types and methods to operate upon the object.

In JScript, the types are not defined.

Constructor functions: A function which is to be used with the new operator is called a constructor function or simply constructor. Its job is to initialize a newly created object with any properties that need to be set before the object can be used. It usually does not have a return value but is allowed to return an object value which becomes the value of the new expression.

Prototype objects:

In JScript, every object includes an internal reference to another object called as its prototype object. Any properties of the prototype will appear as properties of the object for which it is the prototype.

With this information, we are all set to implement our first Class in JScript.

We want to write a class Rectangle, which has width and height as its data and a method area that computes the area.

First step would be to define the class. But there's no Class keyword supported by JScript. So how do we do it? This is how we do it:

Define a constructor function which initializes the required data.

function Rectangle (ht, wt) {

this.height = ht;

this.width=wt;

}

Next would be to create objects of type Rectangle.

var rect1 = new Rectangle(5,10);

var rect2 = new Rectangle(2,4);

Then define a function which computes the area for the rectangle object.

function computeArea(rObj) {

return rObj.height * r)bj.width;

}

Call the function to compute area.

var rectArea = computeArea(rect1); //will return 50.

But this is not object-oriented in it's strict sense. A better approach would be to invoke a method on the Rectangle object rather than passing the object to a function. This can be done by adding a method in the definition of the class. The Rectangle constructor would now look like:

function Rectangle (ht, wt) {

this.height = ht;

this.width=wt;

this.area = function() { return this.height * this.width;}

}

And the way area can be calculated for rect obj is:

var rect1Area = rect1.area(); // rect1.area() will return 50.

var rect2Area = rect2.area(); // rect2.area() will return 8.

This is a much better object oriented implementation but still not the most optimal. Here every Rectangle object created will have three properties - height, width and area. height and width for each Rectangle object might differ but area property would always refer to the same function object. What this means is area property is going to be common for any number of Rectangle objects. Then why can't we have area as a shared property?

We can and this is where prototype objects come into picture:

Let's define area as a property to the prototype object of Rectangle

Rectangle.prototype.area = function() {return this.height * this.width;}

rect1.area(); // will return 50.

rect2.area(); // will return 8.

From all that we did so far we gather the following information:

Constructor provides the name for the Class and initializes the properties which can be different in every instance of the Class. The prototype object for Rectangle is associated with its constructor and any properties defined by the prototype object are inherited by each and every object created using the constructor. Hence prototype object is the best place to define methods like area which remain the same for all Rectangle objects.

This has couple of advantages:

· Since all common methods (ones that can be shared by all objects) are defined as properties to the prototype object and are directly inherited by the objects, there is good amount of decrease in memory usage.

· In case a property is added to the prototype object after an object was created, then too the object would inherit the newly added property.

Though all objects can read the properties of their prototype object, they are not allowed to modify any. Why is this so?

If rect1 goes and modifies area property then any other object (rect2, rect3...) also get affected as they too would call the same area property to compute the area. Hence none of the objects are allowed to modify the properties defined on the prototype object.

This is just the beginning for Classes in JScript. In the later posts I would discuss the differences among Instance Properties & Class Properties, Instance Methods & Class Methods. Another thing we will be looking at will be data encapsulation and inheritance.

Hope you enjoyed this blog.

Thanks,

Ritesh

SDET, JScript Team


 
 

Things you can do from here:

 
 

WPF Application List



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by ivom on 9/24/07

A couple of months ago I met with a few interns as part of a small networking event held at the Microsoft company museum. I wanted to show them a particularly cool WPF XBAP developed by an external company, but I could not remember the correct URL and I couldn't find the right WPF app list out there that had a pointer to that application... So I decided to start my own shiny WPF application list, that I will evolve with time. I am also providing pointers to the existing app lists out there that I am aware of.

WPF is getting great adoption, so I am pretty sure that very soon all of these lists will become meaningless, crushed by the sheer number of available applications. Until then...

Application Type Notes
Asahiyama Zoo XBAP The portal of one of the largest Zoos in Japan.
Daily Mail eReader Standalone Newsreader application from Daily Mail (one of the largest British newspapers).
Flotzam Standalone A mash-up of Facebook, Flickr, Twitter and blogs, created by Tim Sneath and Karsten Januszewski (our WPF evangelists)
Forbes.com Reader Standalone Newsreader application from Forbes.
Metaliq Standalone Snowboarding data visualization.
Sawdust XBAP A 3D workbench application.
Valil's Chess XBAP, Standalone A chess game developed by Valentin Iliescu
Windows Vista Magazine XBAP Newsreader application from
Zurich Airport XBAP A fascinating web application showing live data from the Zurich Airport.

Existing Lists


 
 

Things you can do from here:

 
 

星期日, 九月 23, 2007

Why should we care about symbols?



 
 

Hudong 通过 Google 阅读器发送给您的内容:

 
 

于 07-9-23 通过 MSDN Blogs 作者:carloc

I already touched this topic a while ago, but since it's an important part of the debugging process (and your debugging techniques may vary a lot, depending if you have or not good symbols for your dump) I though would be a good idea to give some more details. And just to jump start on the topic, here's something I learnt a while ago after wasting a few hours typing commands and looking at inconsistent results... debugging with the wrong symbols could be much worse than debugging with no symbols at all.

What are symbols?

You can think of symbol files basically as small databases, files which contain source, line information, data types, variables, functions and everything else needed to provide names for all lines of code, instead of hexadecimal addresses. They usually have .pdb (or .dbg) extension, and are matched with the actual executable code using an internal timestamp, so it's very important to generate symbols every time you build the application, also for release builds. If you'll ever have a problem with your live application and you'll need to debug it, and if you'll not have the matching symbols (matching means the symbols obtained from the same exact build of the dlls you put in production) you could be in troubles... Building once again the application to create the symbol files, even without changing your code does not really help because the timestamp will not match, and WinDbg will complain about missing symbols anyway...

generate_debug_information

Note that in Visual Studio if you set "Release" in the "Standard" toolbar, the "Generate debugging information" checkbox will automatically be cleared, so remember to go to the Project properties and flag it again before rebuilding the project.

Since in ASP.NET 2.0 we have a new default page architecture, there is no real need to create the .pdb files unless you're using the "Web Application Project" template which comes with the Service Pack 1 for Visual Studio 2005, in which case you can find it from the project properties, "Compile" tab.

advanced_compiler_settings

The same applies for Visual Studio 2008.

How can we use symbols?

When you open a dump within WinDbg and you type in a command for example to inspect the call stack, the debugger will start looking for matching symbols to show you an output as detailed as possible: but how does it decide where to look for those symbols? It will use the path(s) specified in the "Symbol Search Path" dialog you can find under the File menu (CTRL+S is the keyboard shortcut.

windbg_symbol_search_pathHere you can specify the symbol servers where WinDbg can download the symbols from, and of course you can use has many server more than one server at a time; WinDbg will simply access those servers in the same order you put in the search path, and it goes through the end of the list until it finds a match.

Microsoft has a public symbol server accessible through the Internet which stores public .pdb files for almost all of our products: http://msdl.microsoft.com/download/symbols.

I work a lot with WinDbg and memory dumps in my daily job, also with my laptop when not connected to the Internet or corporate network, so I need to be able to debug while offline and in any case I don't want to waste time waiting for the tool to download the same symbols over and over again, through one dump to the other... for this reason it's also possible to create a local symbol cache (also referred as downstream store) on your hard disk. When looking for symbols, WinDbg will first of all check your local cache and if the match is found there no additional check is done against the other symbol servers, while if the match is not found WinDbg goes on as usual until it finds the right match; in this case it downloads the .pdb and stores it in your local symbol cache, so that next time it will be readily available for you.

The symbol path is a string composed of multiple directory paths, separated by semicolons. For each directory in the symbol path, the debugger will look in three directories: for instance, if the symbol path includes the directory c:\MyDir, and the debugger is looking for symbol information for a dll, the debugger will first look in c:\MyDir\symbols\dll, then in c:\MyDir\dll, and finally in c:\MyDir. It will repeat this for each directory in the symbol path. Finally, it will look in the current directory, and then the current directory with \dll appended to it. (The debugger will append dll, exe, or sys, depending on what binaries it is debugging.)

Here is a sample symbol path:

SRV*c:\symbols*\\internalshare\symbols*http://msdl.microsoft.com/download/symbols

The above is actually the symbol path (a bit simplified) I use on my machines: as you can see I have a local cache in C:\Symbols; if I've never downloaded a particular symbol before, WinDbg does to an internal share were we have full symbols, and if still unsuccessful I finally give a try to the public Microsoft symbol server on the Internet. If you include two asterisks in a row where a downstream store would normally be specified, then the default downstream store is used. This store will be located in the sym subdirectory of the home directory. The home directory defaults to the debugger installation directory; this can be changed by using the !homedir extension. If the DownstreamStore parameter is omitted and no extra asterisk is included (i,e. if you use srv with exactly one asterisk or symsrv with exactly two asterisks) then no downstream store will be created and the debugger will load all symbol files directly from the server, without caching them locally. Note that If you are accessing symbols from an HTTP or HTTPS site, or if the symbol store uses compressed files, a downstream store is always used. If no downstream store is specified, one will be created in the sym subdirectory of the home directory.

The symbol server does not have to be the only entry in the symbol path. If the symbol path consists of multiple entries, the debugger checks each entry for the needed symbols; moreover the symbol path can contain several directories or symbol servers, separated by semicolons. This allows you to locate symbols from multiple locations (or even multiple symbol servers). If a binary has a mismatched symbol file, the debugger cannot locate it using the symbol server because it checks only for the exact parameters. However, the debugger may find a mismatched symbol file with the correct name, using the traditional symbol path, and successfully load it; in this case it's important to know if our symbols matches (see the next topic).

You can set the symbol path in advance once for all within WinDbg: open an empty instance, press CTRL+S, type in the path, clock "Ok" on the dialog and close WinDbg, accepting to save the workspace if prompted to do so (next time you'll open WinDbg the value will still be there). Or you can use the .sympath command within WinDbg with a dump open.

Another option is to set the system wide variable _NT_SYMBOL_PATH (the syntax is still the same), used by debuggers like WinDbg or also from adplus directly when it captures the dump.

The same principle applies to the Visual Studio debugger (also have a look at the article http://support.microsoft.com/kb/311503/en-us):

symbols_vs2005

How can I check if my symbols matches?

Looking at a call stack sometimes it's clear you're having a problem with unmatched symbols because WinDbg tells you something like:

 1: ChildEBP RetAddr 
 2: 0012f6dc 7c59a2d1 NTDLL!NtDelayExecution(void)+0xb
 3: 0012f6fc 7c59a29c KERNEL32!SleepEx(unsigned long dwMilliseconds = 0xfa, int bAlertable = 0)+0x32
 4: *** ERROR: Symbol file could not be found. Defaulted to export symbols for aspnet_wp.exe - 
 5: 0012f708 00442f5f KERNEL32!Sleep(unsigned long dwMilliseconds = 0x444220)+0xb
 6: WARNING: Stack unwind information not available. Following frames may be wrong.
 7: 0012ff60 00444220 aspnet_wp+0x2f5f
 8: 0012ffc0 7c5989a5 aspnet_wp!PMGetStartTimeStamp+0x676
 9: 0012fff0 00000000 KERNEL32!BaseProcessStart(<function> * lpStartAddress = 0x004440dd)+0x3d

Unfortunately could happen to not be so lucky, and you'll find yourself wondering if the stack you are looking at is genuine or there are some small (or maybe even not so small) inconsistencies which may lead you down to a completely wrong path. In such cases, you can first of all use the lm command to find which .pdb files have been loaded:

 1: kernel32 (pdb symbols) .sympath SRV\kernel32.pdb\CE65FAF896A046629C9EC86F626344302\kernel32.pdb
 2: ntdll (pdb symbols) .sympath SRV\ntdll.pdb\36515FB5D04345E491F672FA2E2878C02\ntdll.pdb
 3: shell32 (deferred)
 4: user32 (deferred)

As you can see in the example above, two symbols were loaded (for kernel32.dll and ntdll.dll), while shell32.dll and user32.dll were not part of the stack analyzed, so WinDbg has not loaded yet (deferred) their symbols. A bad match will look like the following:

 1: ntdll M (pdb symbols) .sympath SRV\ntdll.pdb\36515FB5D04345E491F672FA2E2878C02\ntdll.pdb

Notice the "M" highlighted in red (could also be a "#" pound sign)? That stands or "mismatch", and indicates there is a problem with that particular module (search for "Symbol Status Abbreviations" in WinDbg help for further details). Alternatively you can use the !sym noisy and .reload command to reload symbols verbosely to have a detailed output. Look for "Symbols files and paths - Overivew" in WinDbg help for more details.

Trick: you have the right symbol, but WinDbg does not matches it anyway...

I'm not sure why this happens, and especially why I had this problem only with ntdll.dll (and its .pdb): I was not able to get the proper stack even with a matching symbol (and I checked more than once to be really sure)... until I got the idea to delete the ntdll.pdb folder in my local cache (if you have a dump open you must first unload the symbol from WinDbg or the file will be locked: use the .reload /u <module_name> command), then run a .reload /f <module_name> (/f forces immediate symbol load) and let WinDbg to download it again... this usually does the trick and I finally get the correct stack.

Debugging without symbols?

It's not impossible, but it's harder than debugging with matching symbols; the main difference is that you'll not be able to see method names, variable names etc... and generally speaking the stack will be less easily readable. To give you a quick example, here is an excerpt of the stack of a very simple application I wrote for test (it has just a button which sets the text of a label to the DateTime.Current.ToString()):

without symbols:

 1: 5 Id: 10d4.1204 Suspend: 1 Teb: 7ffd7000 Unfrozen
 2: ldEBP RetAddr 
 3: NING: Frame IP not in any known module. Following frames may be wrong.
 4: 9f524 71a6b7f8 0x7c90eb94
 5: 9fa0c 03490657 0x71a6b7f8
 6: WARNING: Unable to verify checksum for System.dll
 7: ERROR: Module load completed but symbols could not be loaded for System.dll
 8: 9fa40 7a603543 CLRStub[StubLinkStub]@3490657(<Win32 error 318>)
 9: a8240 032908ff System!System.Net.Sockets.Socket.Accept(<HRESULT 0x80004001>)+0xc7
 10: ERROR: Module load completed but symbols could not be loaded for WebDev.WebHost.dll
 11: 9fab0 7940a67a WebDev_WebHost!Microsoft.VisualStudio.WebHost.Server.OnStart(<HRESULT 0x80004001>)+0x27
 12: WARNING: Unable to verify checksum for mscorlib.dll
 13: ERROR: Module load completed but symbols could not be loaded for mscorlib.dll
 14: bd1b4 7937d2bd mscorlib!System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(<HRESULT 0x80004001>)+0x1a
 15: bd1b4 7940a7d8 mscorlib!System.Threading.ExecutionContext.Run(<HRESULT 0x80004001>)+0x81
 16: 9fae0 7940a75c mscorlib!System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(<HRESULT 0x80004001>)+0x44
 17: 32010 79e79dd3 mscorlib!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(<HRESULT 0x80004001>)+0x60
 18: 9fb04 79e79d57 0x79e79dd3
 19: 9fb84 79f71cba 0x79e79d57
 20: 9fba4 79f71c64 0x79f71cba
 21: 9fc08 79f71cf3 0x79f71c64
 22: 9fc3c 7a0b0896 0x79f71cf3
 23: 9fc9c 79f7ba4f 0x7a0b0896
 24: 9fcb0 79f7b9eb 0x79f7ba4f
 25: 9fd44 79f7b90c 0x79f7b9eb
 26: 9fd80 79ef9887 0x79f7b90c
 27: 9fda8 79ef985e 0x79ef9887
 28: 9fdc0 7a0a32da 0x79ef985e
 29: 9fe28 79ef938f 0x7a0a32da
 30: 9fe94 79f7be67 0x79ef938f
 31: 9ffb4 7c80b683 0x79f7be67
 32: 9ffec 00000000 0x7c80b683

with matching symbols:

 1: 5 Id: 10d4.1204 Suspend: 1 Teb: 7ffd7000 Unfrozen
 2: ldEBP RetAddr 
 3: 9f4e4 7c90e9c0 ntdll!KiFastSystemCallRet
 4: 9f4e8 71a54033 ntdll!ZwWaitForSingleObject+0xc
 5: 9f524 71a6b7f8 mswsock!SockWaitForSingleObject+0x1a0
 6: 9f9bc 71ac0e2e mswsock!WSPAccept+0x21f
 7: 9f9f0 71ac103f ws2_32!WSAAccept+0x85
 8: 9fa0c 03490657 ws2_32!accept+0x17
 9: WARNING: Unable to verify checksum for System.ni.dll
 10: 9fa40 7a603543 CLRStub[StubLinkStub]@3490657(<Win32 error 318>)
 11: a8240 032908ff System_ni!System.Net.Sockets.Socket.Accept(<HRESULT 0x80004001>)+0xc7
 12: 9fab0 7940a67a WebDev_WebHost!Microsoft.VisualStudio.WebHost.Server.OnStart(<HRESULT 0x80004001>)+0x27
 13: WARNING: Unable to verify checksum for mscorlib.ni.dll
 14: bd1b4 7937d2bd mscorlib_ni!System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(<HRESULT 0x80004001>)+0x1a
 15: bd1b4 7940a7d8 mscorlib_ni!System.Threading.ExecutionContext.Run(<HRESULT 0x80004001>)+0x81
 16: 9fae0 7940a75c mscorlib_ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(<HRESULT 0x80004001>)+0x44
 17: 32010 79e79dd3 mscorlib_ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(<HRESULT 0x80004001>)+0x60
 18: 9fb04 79e79d57 mscorwks!CallDescrWorker+0x33
 19: 9fb84 79f71cba mscorwks!CallDescrWorkerWithHandler+0xa3
 20: 9fba4 79f71c64 mscorwks!DispatchCallBody+0x1e
 21: 9fc08 79f71cf3 mscorwks!DispatchCallDebuggerWrapper+0x3d
 22: 9fc3c 7a0b0896 mscorwks!DispatchCallNoEH+0x51
 23: 9fc9c 79f7ba4f mscorwks!QueueUserWorkItemManagedCallback+0x6c
 24: 9fcb0 79f7b9eb mscorwks!Thread::DoADCallBack+0x32a
 25: 9fd44 79f7b90c mscorwks!Thread::ShouldChangeAbortToUnload+0xe3
 26: 9fd80 79ef9887 mscorwks!Thread::ShouldChangeAbortToUnload+0x30a
 27: 9fda8 79ef985e mscorwks!Thread::ShouldChangeAbortToUnload+0x33e
 28: 9fdc0 7a0a32da mscorwks!ManagedThreadBase::ThreadPool+0x13
 29: 9fe28 79ef938f mscorwks!ManagedPerAppDomainTPCount::DispatchWorkItem+0xdb
 30: 9fe3c 79ef926b mscorwks!ThreadpoolMgr::ExecuteWorkRequest+0xaf
 31: 9fe94 79f7be67 mscorwks!ThreadpoolMgr::WorkerThreadStart+0x223
 32: 9ffb4 7c80b683 mscorwks!Thread::intermediateThreadProc+0x49
 33: 9ffec 00000000 kernel32!BaseThreadStart+0x37

The difference is quite obvious... The WinDbg help file also gives a few hints:

  1. To figure out what the addresses mean, you'll need a computer which matches the one with the error. It should have the same platform (x86, Intel Itanium, or x64) and be loaded with the same version of Windows
  2. When you have the computer configured, copy the user-mode symbols and the binaries you want to debug onto the new machine
  3. Start CDB or WinDbg on the symbol-less machine
  4. If you don't know which application failed on the symbol-less machine, issue an | (Process Status) command. If that doesn't give you a name, break into KD on the symbol-less machine and do a !process 0 0, looking for the process ID given by the CDB command
  5. When you have the two debuggers set up -- one with symbols which hasn't hit the error, and one which has hit the error but is without symbols -- issue a k (Display Stack Backtrace) command on the symbol-less machine
  6. On the machine with symbols, issue a u (Unassemble) command for each address given on the symbol-less stack. This will give you the stack trace for the error on the symbol-less machine
  7. By looking at a stack trace you can see the module and function names involved in the call

I got symbols from my customer: what should I do now?

The easiest thing you could do is use symstore.exe (you'll find it in WinDbg/adplus installation folder) with a command like the following:

symstore add /f c:\temp\SymbolsTest\Bin\*.pdb /s c:\symbols /t "Symbols Test"

Let's have a quick look at the syntax:

  • The "add" keyword is quite self explanatory smile_wink
  • /f tells symstore which is the file you want to add; note you can use wildcards, so you can add multiple files at once
  • /s is the path to your symbols store (this will most likely be your local cache, or your shared symbol server)
  • /t a required description for the symbol to store

Symstore will create a structure similar to the following:

symstore_test

Also note the "0000Admin" folder created by symstore, which contains one file for each transaction (every "add" or "delete" operation is recorded as a transaction), as well as the logs server.txt and history.txt; the former contains a list of all transactions currently on the server, while the latter contains a chronological history of all transactions run on the machine. For further information you can see the "Using SymStore" topic in WinDbg help (debugger.chm).

Conclusion

While it is possible to debug without symbols (this is true especially for managed code), remember that your life could be much easier if you (and your customers) will take care of your symbols and will generate them every time the application will be rebuilt, also in release mode.

I deliberately simplified the argument (I just wanted to share what I learnt in my daily job and give some quick tips to get started), much more could be said and if you're interested I encourage you to read the "Symbols" section in the WinDbg help, or search the Internet where you'll find good blog posts/articles on this subject smile_nerd

Carlo


 
 

可从此处完成的操作: