This is an archive of the discontinued LLVM Phabricator instance.

[lldb] fix memory leak in "GetGDBServerRegisterInfoXMLAndProcess"
ClosedPublic

Authored by yuanzi on Jan 5 2022, 4:03 PM.

Details

Summary

While running heap checker on a test that uses LLDB API, the following memory leak is found:
RAW: HeapChecker started...
RAW: Leak check _main_ detected leaks of 34 bytes in 4 objects
RAW: The 2 largest leaks:
RAW: Leak of 17 bytes in 2 objects allocated from:
@ 0x7fb93bd20166 NewHook()
@ 0x7fb929372a73 absl::base_internal::MallocHook::InvokeNewHookSlow()
@ 0x5600d1046093 libc_malloc
@ 0x7fb974529c03 xmlStrdup
@ 0x7fb9744c2a0b xmlGetProp
@ 0x7fb9749d9ed6 lldb_private::XMLNode::GetAttributeValue()
@ 0x7fb979043001 std::
u::function::policy_invoker<>::__call_impl<>()
@ 0x7fb9749da06d lldb_private::XMLNode::ForEachChildElement()
@ 0x7fb97903c54d lldb_private::process_gdb_remote::ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess()
@ 0x7fb97902cfe4 lldb_private::process_gdb_remote::ProcessGDBRemote::GetGDBServerRegisterInfo()
@ 0x7fb97902c1d0 lldb_private::process_gdb_remote::ProcessGDBRemote::BuildDynamicRegisterInfo()
@ 0x7fb97902e92a lldb_private::process_gdb_remote::ProcessGDBRemote::SetThreadStopInfo()
@ 0x7fb97902db18 lldb_private::process_gdb_remote::ProcessGDBRemote::DoConnectRemote()
@ 0x7fb97584965e lldb_private::Process::ConnectRemote()
@ 0x7fb975839fa6 lldb_private::Platform::DoConnectProcess()
@ 0x7fb97583a39e lldb_private::Platform::ConnectProcessSynchronous()
@ 0x7fb97545b28b CommandObjectProcessConnect::DoExecute()
@ 0x7fb9755a70c9 lldb_private::CommandObjectParsed::Execute()
@ 0x7fb97559c0e9 lldb_private::CommandInterpreter::HandleCommand()
@ 0x7fb975460145 lldb_private::CommandObjectRegexCommand::DoExecute()
@ 0x7fb9755a72d2 lldb_private::CommandObjectRaw::Execute()
@ 0x7fb97559c0e9 lldb_private::CommandInterpreter::HandleCommand()
@ 0x7fb997a5f22e lldb::SBCommandInterpreter::HandleCommand()
@ 0x7fb997a5ef9b lldb::SBCommandInterpreter::HandleCommand()

This change fixes the memory leaks by freeing memory after it is no longer in use.
Tested with "ninja check-lldb".

Diff Detail

Event Timeline

yuanzi requested review of this revision.Jan 5 2022, 4:03 PM
yuanzi created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptJan 5 2022, 4:03 PM
JDevlieghere requested changes to this revision.Jan 5 2022, 5:19 PM

We shouldn't have to manage memory at this granularity. Why isn't xmlFreeDoc cleaning this up?

This revision now requires changes to proceed.Jan 5 2022, 5:19 PM
labath added a comment.Jan 6 2022, 5:21 AM

We shouldn't have to manage memory at this granularity. Why isn't xmlFreeDoc cleaning this up?

Because xmlGetProp returns a value that's independent of the containing document.

That said, I agree this is a pretty bad encapsulation breakage (and will not compile in !LLDB_ENABLE_LIBXML builds).

One way to fix this (if we wanted to be fancy) is to have this function return a std::unique_ptr<char> with a custom deleter (xmlFree), but given that this is hardly performance sensitive code, I would just copy the attribute value into a std::string inside GetAttributeValue, and then have it return *that* (after freeing the original string).

In either case, this function only has a handful of callers, so it should be pretty easy to update it to use a saner API.

yuanzi updated this revision to Diff 398009.EditedJan 6 2022, 3:53 PM
yuanzi edited the summary of this revision. (Show Details)

Yeah since xmlGetProp calls xmlGetPropNodeValueInternal, which calls xmlStrdup rather than returning the content or value of the node directly, xmlFreeDoc could not clean it up.
https://github.com/tenderlove/libxml2/blob/master/tree.c

Agree that we need to update the API to return not just a reference to string (llvm::StringRef), but an object that can own string data (std::string).

This change fixes the memory leaks in GetGDBServerRegisterInfoXMLAndProcess by updating GetAttributeValue to return std::string instead of llvm::StringRef.
Tested with ninja check-lldb.

Yes, that's the API I had in mind, but check out the inline comments for some problems/improvements.

lldb/source/Host/common/XML.cpp
141

this should be xmlFree, per the function documentation.

157–159

I don't see why we need these temporary variables. std::string is implicitly convertible to a StringRef, and you don't need the value afterwards, so you should be able to pass the GetAttributeValue straight into the to_integer function. Did that not work for some reason?

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
4534

Same here. .empty() and to_integer should work on std::string as well. Constructing a StringRef might make sense if we needed to perform some more complex operations (ones which std::string does not support) but I don't see anything like that here.

yuanzi updated this revision to Diff 398193.Jan 7 2022, 10:45 AM

free->xmlFree, remove conversions from std::string to llvm::StringRef.

yuanzi marked 3 inline comments as done.Jan 7 2022, 10:52 AM
yuanzi added inline comments.
lldb/source/Host/common/XML.cpp
157–159

I did that mainly to ensure the new code behaves exactly the same as the old. Since no complex functions are called, they should work interchangeably! Reverted to previous version.

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
4534

Right! Done.

yuanzi marked 2 inline comments as done.Jan 7 2022, 10:53 AM

Thanks! LGTM

labath accepted this revision.Jan 10 2022, 1:42 AM

Cool, thanks. Do you need someone to commit this for you?

Thanks for the reviews!

Yeah I do not have commit access. It would be great if someone could help with the commit!

This revision was not accepted when it landed; it landed in state Needs Review.Jan 10 2022, 2:33 PM
This revision was automatically updated to reflect the committed changes.