Closed GoogleCodeExporter closed 9 years ago
Ok, a new problem: now that we patch everything, there's nothing left to serve
as a
dependency between libtcmalloc_minimal.dll and the main executable. That is,
if you
have a problem like
main() { malloc(1); }
and link it will libtcmalloc_minimal.dll, the linker will decide libtcmalloc
doesn't
add anything, and just ignore it at link time. The end result is malloc won't
get
patched.
The reason this didn't happen before, I think, is that libtcmalloc_minimal
overrode
operator new, which mscvrt uses (I guess?), and that yielded the necessary
dependency.
There are various ways to work around this (including the /INCLUDE linker
flag), but
they're all pretty ugly. The way I'd like to work around it is to continue to
override operator new/new[]/delete/delete[], rather than patching them. The
plan
here is that I'd just override them in the main module, while still patching
them
inside dll's.
Does this seem like a workable solution? Does it still address the issues that
caused you to want to patch new/delete in the first place?
Original comment by csilv...@gmail.com
on 15 Nov 2008 at 11:19
I believe that change can be dangerous if the user manages to make use of
new/delete
before things are patched (they can have a mismatch between new/delete over dll
boundaries, since the dll's delete won't be able to call tcmalloc's new yet).
I'm
pretty sure the user is not supposed to call new/delete from things like
DLLMain, but
it works in practice so people do it.
I've been using a .obj file in my binary that simply forces a reference to
tcmalloc
to do this. It seems pretty safe -- the user already has to modify their link
line,
and this approach will work with any compiler (e.g., gcc).
Original comment by dvi...@gmail.com
on 16 Nov 2008 at 3:56
I took a look at the new patch_functions.cc. It looks good except for a couple
comments that I found a little misleading.
"This only matters when we're linking statically, and there's no DLLs."
Dynamically loaded instances of libc are not mutually exclusive with respect to
a a
statically linked libc (e.g., if the program statically links with libc and
also uses
advapi32.dll). I think the code is doing the right thing.
Original comment by dvi...@gmail.com
on 16 Nov 2008 at 5:02
I've changed the comment -- thanks for looking it over.
} It seems pretty safe -- the user already has to modify their link line,
} and this approach will work with any compiler (e.g., gcc).
I was hoping to make this as easy as possible for clients, and so was hoping to
avoid
adding a second, non-obvious link argument. Can you explain a bit more about
why the
change could be dangerous? You said:
} they can have a mismatch between new/delete over dll
} boundaries, since the dll's delete won't be able to call tcmalloc's new yet
What do you mean by "over dll boundaries"? Why won't the dll's delete be able
to
call tcmalloc's delete yet? (I assume you meant "tcmalloc's delete" and not
"tcmalloc's new".) I think I'm missing what the problem scenario is.
Original comment by csilv...@gmail.com
on 16 Nov 2008 at 10:23
Basically, a user would need to invoke new from the executable before tcmalloc
has an
opportunity to patch things. They would need to proceed to pass a pointer to
the
new'd object to a DLL that did not explicitly link against tcmalloc's lib file.
This
DLL would need to invoke delete on the pointer. Example:
// foo.exe
// call this from a global constructor
void irunreallyearly(void){
kill(new char);
}
// foo.dll
void __declspec(dllexport) kill(char*p){
delete p;
}
irunreallyearly would need to be invoked by a constructor that runs before
tcmalloc
gets to patch things. I cannot find documentation regarding the order in which
global constructors run in Visual C, and we don't need to rely on undefined
behavior
if we do not define new. The only thing I've come across with respect to
global
constructor order are some forum posts claiming it isn't defined between
different
compilation units.
Another possible problem here is that tcmalloc may just crash if new is invoked
before tcmalloc's constructors are invoked. This isn't so good either, and the
possibility can be avoided by not defining new.
Original comment by dvi...@gmail.com
on 25 Nov 2008 at 2:18
OK. I will make it so you have to use /INCLUDE when linking in tcmalloc.
Hopefully
folks won't mind too much!
Original comment by csilv...@gmail.com
on 25 Nov 2008 at 2:56
OK, we're getting closer! I have sign-off here on the final version of the
patch_functions patch, so we're only left with a few small things in tcmalloc
and
friends. Mostly they have to do with code that runs before patching, or with
unpatching.
My next question is about the following, in heap_profiler.cc:
// On windows, new will not have been called yet (since we haven't
// patched stuff) so call InitialMallocHook_New here to prevent an
// assertion failure on the following lines.
InitialMallocHook_New(NULL,0);
What is the exact problem here that this code is solving? Why haven't we
patched
stuff yet? Don't we patch like the very first thing we do?
I think this is related to the problem you addressed elsewhere in the patch,
where
the heap-profiler initialization might run before tcmalloc's. I've fixed that,
so I
think this code is no longer needed. Can you tell me how to reproduce the
problem
that prompted this change, so I can verify that problem is fixed in the current
codebase?
Original comment by csilv...@gmail.com
on 1 Dec 2008 at 9:15
I'm not sure what it is about (some of?) our binaries that causes the
compilation
units to initialize in a different order. Any chance I could get a patch for
the
initialization order fix and test it here? It sounds like you've probably
taken care
of it.
Original comment by dvi...@gmail.com
on 3 Dec 2008 at 8:48
Hmm, I'm trying to remember what we did -- it was a while ago, and for a
different
reason.
The key was to put this before the REGISTER_MODULE_INITIALIZER in
heap-profiler.cc:
static const TCMallocGuard tcmalloc_initializer;
For this to work, TCMallocGuard has to be moved from tcmalloc.cc into a .h
file. I
just moved the declaration by creating a new .h file that looked like this:
class TCMallocGuard {
public:
TCMallocGuard();
~TCMallocGuard();
};
and declaring that .h file at the top of heap_profiler.cc. I had to mess with
a few
things in tcmalloc.cc, but that's the gist of it. Is that enough to test your
change? Sorry I can't come up with a real patch very easily; this was combined
with
some other things that are difficult to tease out (and wouldn't apply cleanly).
Original comment by csilv...@gmail.com
on 3 Dec 2008 at 10:36
OK, assuming this heap-profiler issue is ok, there's only one remaining
question I
have, and then the patch will be fully applied! That question has to do with
this
change to port.h:
~SpinLock() {
- perftools_pthread_once(&initialize_token_, InitializeMutex);
- DeleteCriticalSection(&mutex_);
+ /* dvitek: Because global destructor order is undefined, this
+ * causes serious problems (crashes after main finishes) if
+ * someone tries to use the lock after this destructor fires.
+ */
+ // perftools_pthread_once(&initialize_token_, InitializeMutex);
+ // DeleteCriticalSection(&mutex_);
}
Can you talk more to what crashes you were seeing here? Based on the comment, I
would expect to see this happen only if a SpinLock were defined in one class
and then
used in another, which I don't think happens.
Perhaps the problem is that a spinlock is deleted in the main thread, while
other
threads are still running? That could cause the crash you mention. Anyway,
more
details about what prompted this will make it easier to understand the right
fix.
Original comment by csilv...@gmail.com
on 6 Dec 2008 at 1:57
Never mind about that last comment -- I just hand-verified that all SpinLocks in
perftools live the entire length of the program, so leaking memory in the
destructor
is perfectly fine. So I think, modulo the heap-profiler issue, we're good to
go!
Original comment by csilv...@gmail.com
on 6 Dec 2008 at 2:06
Perftools 1.0rc1 is out, with (almost) all the content in this patch applied!
Thanks
so much for your contribution. Let me know how the release works for you.
Original comment by csilv...@gmail.com
on 13 Dec 2008 at 1:46
I'm working to get pprof running on cygwin, and this line in pprof is giving me
trouble:
if ($l =~
/^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|exe)(\.\d+)*\w*)
/i) {
(It decides which lines from the /proc/self/maps output to pay attention to.)
By design, this line is supposed to match libraries. However, the 'exe'
extension is
causing it to match the main executable, which is leading to trouble.
pprof catches the main executable later in the function:
# Append special entry for the main program. ...
Do you remember why you added "|exe" to the regexp in your patch
http://google-perftools.googlecode.com/issues/attachment?aid=753171857806843247&
name=google-perftools-0.99.2.msvc8_i386_hprof.patch?
I know it was a long time ago. If we can't figure out the reason why, I'm going to
remove it for the next release (perftools 1.2).
Original comment by csilv...@gmail.com
on 13 Mar 2009 at 7:17
I don't remember off the top of my head. I'll see if anything bad happens when
I
remove it.
Original comment by dvi...@gmail.com
on 16 Mar 2009 at 4:47
seems to work okay without it. At this point, my best guess is that it was
added at
some point when the later code for dealing with the main executable wasn't
working
yet.
Original comment by dvi...@gmail.com
on 17 Mar 2009 at 3:45
Great! I'll take it out then.
Original comment by csilv...@gmail.com
on 17 Mar 2009 at 5:47
Original issue reported on code.google.com by
dvi...@gmail.com
on 16 Oct 2008 at 11:27Attachments: