Effective use of base::debug::Alias

64 views
Skip to first unread message

Bruce Dawson

unread,
Aug 18, 2017, 6:25:01 PM8/18/17
to Chromium-dev
I noticed some incorrect uses of base::debug::Alias which led me to file a couple of bugs (crbug.com/756589 and crbug.com/756544) and update the comment block in front of the function. But I also wanted to write a quick PSA about how to use this function effectively.

If you pass the address of a local variable to the Alias() function this "fools" the compiler into thinking that the local variable is being externally referenced, and it prevents the compiler from optimizing away or discarding the local variable. This then means that the local variable will still be present on the stack, right to the end of the enclosing scope. This can be used to ensure that interesting local variables are present in crash dumps.


However, the Alias() function isn't magic. It has an empty implementation and its magic only works because the compiler doesn't know that. As such, there are many things that it cannot do:

1) If the local variable whose address you pass is a pointer then the value of that pointer will be on the stack. However the memory that it points to will probably not be. Crash dumps generally only record stack memory, not heap memory or global variables.
2) If you pass the address of a member variable or global variable then the Alias() function will have no effect. They were never going to be optimized away so the Alias() call is a NOP.
3) If you pass the return value of a function then the Alias() function will have no effect - there is nothing to avoid optimizing away.

To use the Alias() function effectively you must copy the state that you care about into POD locals and call Alias() on them (before or after, it doesn't matter), and then trigger a crash dump before they go out of scope.

If you can think like a compiler and if you know that minidumps generally only record stack memory then this will all make sense, but if not just remember that Alias(&local) will retain the value of local on the stack, and nothing else.

It is possible to add additional memory ranges to a crash dump, however such a capability would have to be used much more carefully than the cheap-and-simple trick of the Alias() function.

I hope this is helpful.

Ryan Hamilton

unread,
Aug 18, 2017, 6:31:28 PM8/18/17
to bruce...@chromium.org, Chromium-dev
This is great! I wonder if it would be worth putting this up on www.chromium.org/ somewhere, perhaps with some code snippets of do's and don't's? 

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
---
You received this message because you are subscribed to the Google Groups "Chromium-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-dev/CAE5mQiNeA7J%2BCdSPz8xL8DGRCnN1%3DmnmQSmdfLDtoat17VY5_Q%40mail.gmail.com.

Daniel Cheng

unread,
Aug 18, 2017, 6:33:37 PM8/18/17
to r...@chromium.org, bruce...@chromium.org, Chromium-dev
Is it possible to write a helper to wrap base::debug::Alias() to make it more likely that it will do the right thing? Perhaps even a macro? I guess it might be hard to distinguish between when something is already a local or not...

Daniel

Bruce Dawson

unread,
Aug 18, 2017, 6:38:32 PM8/18/17
to Daniel Cheng, r...@chromium.org, Chromium-dev
It would be easy to detect at run-time that a bad address has been passed, by seeing if it is in the range of the current thread's stack. A DCHECK of that would catch some errors. I thought about doing that but felt that a lot of the Alias() calls only happen in error conditions and therefore might never get checked in debug builds. I'm sure that some clang-analysis magic could validate that only locals were passed, but I don't know how much effort would be required.

I think about 20% of the uses were incorrect, based on my (incomplete) code searching.

I felt that documenting the function in the header file was probably the best bet - I'm not sure that documentation elsewhere would be found when needed.

Lucas Gadani

unread,
Aug 18, 2017, 6:47:34 PM8/18/17
to bruce...@chromium.org, Daniel Cheng, Scott Graham, r...@chromium.org, Chromium-dev
+scottmg

I believe crashpad automatically captures a portion of memory around pointers referenced on the stack, so most of the time if you have your heap memory referenced on the stack, their values are captured in the minidump. This makes base::debug::Alias(ptr_to_heap) work most of the time (as long as the object is small enough).


Scott Graham

unread,
Aug 18, 2017, 6:51:38 PM8/18/17
to Lucas Gadani, Bruce Dawson, Daniel Cheng, Ryan Hamilton, Chromium-dev
Yeah, it does try, but how much depends on the particular channel (more in Canary, much less in Stable).

So in general, if you know what data you'd like to see in a crashdump, then I'd use that domain-specific knowledge and follow Bruce's advice to Alias() the important bits into a plain small inline type on the stack.

Claudio deSouza

unread,
Aug 21, 2017, 8:49:58 AM8/21/17
to Chromium-dev
That's good to know... Slightly unrelated though, I have seen claims that the following code would  be broken by certain optimisers:

if (some_ptr)
  some_ptr->GetSomething();
  
So, since some_ptr is being dereferentiated in the line following the conditional statement, I have been told that it is possible the optimiser may decide to just remove the check on the pointer's value (duh!, the pointer is being used in the following line, and its value has not changed, so it must be valid), and this can is one of the few cases that an optimisation can actually introduce UB.

Is this so? Is there any similar resource for such cases? Or should we just use base::WeakPtr for such cases?

Antoine Labour

unread,
Aug 21, 2017, 12:59:43 PM8/21/17
to claudi...@gmail.com, Chromium-dev
Absolutely not, this is not necessary, optimizers don't *introduce* UB, they just expose existing UB in ways that can cause problems when the unoptimized version doesn't.

The pattern that you may be thinking of is:

auto var = some_ptr->field;
// [...] don't use var
if (some_ptr)
  some_ptr->DoSomething();

In that case, the first line tells the compiler (optimizer) that some_ptr is valid, because otherwise it would be UB - regardless of whether 'var' is used or not. It is then allowed to optimize away the 'if (some_ptr)' test.
There's a famous Linux kernel bug that was caused by this pattern (see e.g. https://lwn.net/Articles/342330/).

Antoine

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
---
You received this message because you are subscribed to the Google Groups "Chromium-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev+unsubscribe@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-dev/61452b9d-2541-47c8-a93b-036b3e5391e8%40chromium.org.

Claudio deSouza

unread,
Aug 21, 2017, 1:11:58 PM8/21/17
to Chromium-dev, claudi...@gmail.com
That's what I thought... this is why I said I've been told so, meaning I have a huge measure of scepticism regarding this claim, and never took it seriously.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.

Albert J. Wong (王重傑)

unread,
Aug 21, 2017, 1:37:25 PM8/21/17
to claudi...@gmail.com, Chromium-dev
Optimizations are not supposed to introduce UBs... They're supposed to be valid program transforms.

An optimization can have a bug, or just be badly designed, or not generally applicable, but that's not quite the same thing...

-Albert

Reply all
Reply to author
Forward
0 new messages