-
Notifications
You must be signed in to change notification settings - Fork 13.5k
lldb::SBType::GetFieldAtIndex returning static field members. #55040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@llvm/issue-subscribers-lldb |
How are you getting the type's elements? The "fields" property on SBType doesn't include static fields, it's implementation uses the clang::RecordDecl::field_iterator which omits static members. So you shouldn't need to tell whether an SBTypeMember is static or not in the SBType.members list, because it shouldn't have any entries for the static variables in a class. It seems to me the problem is the opposite, I can't see how you would answer the question "What are the static members of class Foo"...
Jim
… On Apr 22, 2022, at 9:45 AM, Sigurður Ásgeirsson ***@***.***> wrote:
I had a hiccup trying to figure the virtual pointer layout of this class from google/glog <https://github.com/google/glog/blob/master/src/logging.cc#L433>. The heuristic used in the types.py <https://github.com/llvm/llvm-project/blob/main/lldb/examples/python/types.py> example looks for an initial field that starts at a pointer-size offset into the class. When the first field of a class is static, it appears this heuristic will fail, as the static member returns a zero offset.
I haven't found any good way to identify or filter out static members, it appears the data isn't there at all <https://github.com/llvm/llvm-project/blob/main/lldb/include/lldb/Symbol/Type.h#L346>.
Maybe SBTypeMember needs an IsStatic() function, or perhaps GetOffsetInBytes() could return a sentinel value to indicate that the member does not have this pointer offset?
Here's what the "offending" code looks like:
class LogFileObject : public base::Logger {
public:
LogFileObject(LogSeverity severity, const char* base_filename);
~LogFileObject();
...
private:
static const uint32 kRolloverAttemptFrequency = 0x20;
Mutex lock_;
...
};
—
Reply to this email directly, view it on GitHub <#55040>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ADUPVWYEJNCYTN3G7ZHY6ITVGLJRLANCNFSM5UC2URQA>.
You are receiving this because you are subscribed to this thread.
|
There's something deeper going on here and I'm having trouble creating a minimized repro.
outputs:
The DWARF has this:
I guess somehow filtering the static members is derailing for this particular type. I'll see if I can repro this in a local build in a debugger - or maybe this has been fixed already. I'm running V12:
|
Internally, lldb uses the presence of DW_AT_external to identify static members of a type. For instance,
class Foo {
static int g_var = 0;
int m_var;
};
produces:
0x00000045: DW_TAG_class_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("Foo")
DW_AT_byte_size (0x04)
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (1)
0x0000004e: DW_TAG_member
DW_AT_name ("g_var")
DW_AT_type (0x000000000000008f "int")
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (6)
DW_AT_external (true)
DW_AT_declaration (true)
0x00000059: DW_TAG_member
DW_AT_name ("m_var")
DW_AT_type (0x000000000000008f "int")
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (7)
DW_AT_data_member_location (0x00)
In the clang dwarf parser, we test this in ParseSingleMember:
// Handle static members
if (attrs.is_external && attrs.member_byte_offset == UINT32_MAX) {
In your case, it looks like the static variable is a const, so we have:
DW_AT_const_value (0x20)
rather than DW_AT_external. I think that's why we aren't recognizing this as a static member though I didn't follow the parsing through to see where this DIE did get handled.
Jim
… On Apr 22, 2022, at 11:19 AM, Sigurður Ásgeirsson ***@***.***> wrote:
There's something deeper going on here and I'm having trouble creating a minimized repro.
I'm using the C++ API, retrieving fields with SBType::GetFieldAtIndex(), and I'm definitely getting the static member that way. I can repro the same behaviour using the python API:
import lldb
d = lldb.SBDebugger.Create()
t = d.CreateTarget("bazel-bin/symbols/assay_types")
l = t.FindFirstType('google::(anonymous namespace)::LogFileObject')
for f in l.fields:
print(str(f))
outputs:
+0: (typedef gflags::uint32) kRolloverAttemptFrequency
+8: (class Mutex {
...
}) lock_
+56: (_Bool) base_filename_selected_
...
+200: (typedef google::glog_internal_namespace_::WallTime) start_time_
The DWARF has this:
0x000fac6d: DW_TAG_member
DW_AT_name ("kRolloverAttemptFrequency")
DW_AT_decl_file ("/proc/self/cwd/external/com_github_google_glog/src/logging.cc")
DW_AT_decl_line (455)
DW_AT_decl_column (0x17)
DW_AT_type (0x000f900b "const uint32")
DW_AT_declaration (true)
DW_AT_const_value (0x20)
0x000fac7c: DW_TAG_member
DW_AT_name ("lock_")
DW_AT_decl_file ("/proc/self/cwd/external/com_github_google_glog/src/logging.cc")
DW_AT_decl_line (457)
DW_AT_decl_column (0x09)
DW_AT_type (0x000f712f "Mutex")
DW_AT_data_member_location (0x08)
I guess somehow filtering the static members is derailing for this particular type. I'll see if I can repro this in a local build in a debugger - or maybe this has been fixed already. I'm running V12:
$ lldb --version
lldb version 12.0.0
—
Reply to this email directly, view it on GitHub <#55040 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ADUPVW6BAVXJCBGGX5LDQWLVGLUUTANCNFSM5UC2URQA>.
You are receiving this because you are on a team that was mentioned.
|
Sorry for being a little confusing. A close observer would note that the class definition I quoted doesn't compile, since you have to define the static out of the class definition. I was trying to see if defining the static inline would change the DWARF. The actual DWARF was from a compilation that doesn't have the "= 0".
But, then I tried:
class Foo {
const static int g_var = 0;
int m_var;
};
which does compile and would produce a const value like what you were seeing. But with a recent clang, this produces:
0x00000063: DW_TAG_member
DW_AT_name ("g_var")
DW_AT_type (0x00000000000000a5 "const int")
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (6)
DW_AT_external (true)
DW_AT_declaration (true)
DW_AT_const_value (0)
so it has the const value but also the external declaration, and not surprisingly g_var does NOT show up in the members list in the type.
Jim
… On Apr 22, 2022, at 11:32 AM, Jim Ingham ***@***.***> wrote:
Internally, lldb uses the presence of DW_AT_external to identify static members of a type. For instance,
class Foo {
static int g_var = 0;
int m_var;
};
produces:
0x00000045: DW_TAG_class_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("Foo")
DW_AT_byte_size (0x04)
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (1)
0x0000004e: DW_TAG_member
DW_AT_name ("g_var")
DW_AT_type (0x000000000000008f "int")
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (6)
DW_AT_external (true)
DW_AT_declaration (true)
0x00000059: DW_TAG_member
DW_AT_name ("m_var")
DW_AT_type (0x000000000000008f "int")
DW_AT_decl_file ("/tmp/has_static.cpp")
DW_AT_decl_line (7)
DW_AT_data_member_location (0x00)
In the clang dwarf parser, we test this in ParseSingleMember:
// Handle static members
if (attrs.is_external && attrs.member_byte_offset == UINT32_MAX) {
In your case, it looks like the static variable is a const, so we have:
> DW_AT_const_value (0x20)
rather than DW_AT_external. I think that's why we aren't recognizing this as a static member though I didn't follow the parsing through to see where this DIE did get handled.
Jim
> On Apr 22, 2022, at 11:19 AM, Sigurður Ásgeirsson ***@***.***> wrote:
>
>
> There's something deeper going on here and I'm having trouble creating a minimized repro.
> I'm using the C++ API, retrieving fields with SBType::GetFieldAtIndex(), and I'm definitely getting the static member that way. I can repro the same behaviour using the python API:
>
> import lldb
>
> d = lldb.SBDebugger.Create()
> t = d.CreateTarget("bazel-bin/symbols/assay_types")
>
> l = t.FindFirstType('google::(anonymous namespace)::LogFileObject')
> for f in l.fields:
> print(str(f))
> outputs:
>
> +0: (typedef gflags::uint32) kRolloverAttemptFrequency
> +8: (class Mutex {
> ...
> }) lock_
> +56: (_Bool) base_filename_selected_
> ...
> +200: (typedef google::glog_internal_namespace_::WallTime) start_time_
> The DWARF has this:
>
> 0x000fac6d: DW_TAG_member
> DW_AT_name ("kRolloverAttemptFrequency")
> DW_AT_decl_file ("/proc/self/cwd/external/com_github_google_glog/src/logging.cc")
> DW_AT_decl_line (455)
> DW_AT_decl_column (0x17)
> DW_AT_type (0x000f900b "const uint32")
> DW_AT_declaration (true)
> DW_AT_const_value (0x20)
>
> 0x000fac7c: DW_TAG_member
> DW_AT_name ("lock_")
> DW_AT_decl_file ("/proc/self/cwd/external/com_github_google_glog/src/logging.cc")
> DW_AT_decl_line (457)
> DW_AT_decl_column (0x09)
> DW_AT_type (0x000f712f "Mutex")
> DW_AT_data_member_location (0x08)
> I guess somehow filtering the static members is derailing for this particular type. I'll see if I can repro this in a local build in a debugger - or maybe this has been fixed already. I'm running V12:
>
> $ lldb --version
> lldb version 12.0.0
> —
> Reply to this email directly, view it on GitHub <#55040 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ADUPVW6BAVXJCBGGX5LDQWLVGLUUTANCNFSM5UC2URQA>.
> You are receiving this because you are on a team that was mentioned.
>
|
Interesting, I see my attempted repro has the
Out of curiosity - is there a good reason why not to filter on the presence of the |
Not at all - thanks for taking a look and explaining things to me. |
On Apr 22, 2022, at 11:43 AM, Sigurður Ásgeirsson ***@***.***> wrote:
Interesting, I see my attempted repro has the DW_AT_external tag:
0x000023ed: DW_TAG_member
DW_AT_name ("kZero")
DW_AT_decl_file ("/workspaces/thresher/repro.cc")
DW_AT_decl_line (6)
DW_AT_decl_column (0x19)
DW_AT_type (0x000001c3 "const uint32_t")
DW_AT_external (true)
DW_AT_accessibility (DW_ACCESS_public)
DW_AT_declaration (true)
DW_AT_const_value (0x20)
Out of curiosity - is there a good reason why not to filter on the presence of the DW_AT_data_member_location tag?
That's a good question, but for one of our clang DWARF experts, of which I am not one...
Jim
… —
Reply to this email directly, view it on GitHub <#55040 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ADUPVW5YKD6AKAMGWSXZIC3VGLXMBANCNFSM5UC2URQA>.
You are receiving this because you are on a team that was mentioned.
|
Adding some DWARF experts (@dwblaikie). I think there is some confusion in the DWARF spec about the meaning of
which makes the attribute sound completely unrelated to static-ness. However, then it goes on (in an non-normative note) with:
At first glance that may make sense, since the So, I would expect that a static member inside an anonymous namespace does not have the external attribute. However, clang does not do that -- it still emits the attribute. OTOH, gcc does not emit the attribute, so I would be inclined to consider the clang behavior a bug, and try to determine static-ness based on the presence of the |
Ah, interesting. When I was trying to come up with a repro, I missed the fact that the anonymous namespace might be significant. This repros the problem:
assay.py:
Here's the DWARF for the class:
|
I've uploaded a straw man patch that does appear to filter this out. Whether it's a correct fix, I can't say :). |
re: @labath's reading. Yeah, that all sounds right - maybe some history about how the non-normative DWARF spec text could help, but I guess it was just loose language. The GCC behavior probably comes from a simple implementation that inspects the linkage of the variable when deciding whether to put Regardless of what the spec says, supporting what GCC does is probably a good idea - and yeah, it seems better to determine non-static members by the presence/absence of of (as for the original question: Trying to determine if a class is dynamic/has virtual functions based on the offset of the first non-virtual member is probably not the best way to go - instead looking for the virtuality property on the class would be a good idea) |
The example script tests the |
Not sure that guarantees the offset/location of the vtable - I guess that might depend on the inheritance hierarchy, but I'm not sure. I think computing the offset as a side effect of inspecting fields is probably not ideal. In the DWARF itself there's a |
That'd be ideal, though it looks as though LLDB/Clang reconstructs the vtable layout of a class, rather than reading it from the symbols. I'll see if I can coax that info out of the API. |
See [[ #55040 | issue 55040 ]] where static members of classes declared in the anonymous namespace are incorrectly returned as member fields from lldb::SBType::GetFieldAtIndex(). It appears that attrs.member_byte_offset contains a sentinel value for members that don't have a DW_AT_data_member_location. Reviewed By: labath Differential Revision: https://reviews.llvm.org/D124409
@dwblaikie it looks like any class that |
See [[ llvm/llvm-project#55040 | issue 55040 ]] where static members of classes declared in the anonymous namespace are incorrectly returned as member fields from lldb::SBType::GetFieldAtIndex(). It appears that attrs.member_byte_offset contains a sentinel value for members that don't have a DW_AT_data_member_location. Reviewed By: labath Differential Revision: https://reviews.llvm.org/D124409
I had a hiccup trying to figure the virtual pointer layout of this class from google/glog. The heuristic used in the types.py example looks for an initial field that starts at a pointer-size offset into the class to infer that the class has a virtual table pointer. When the first field of a class is static, it appears this heuristic will fail, as the static member returns a zero offset.
I haven't found any good way to identify or filter out static members, it appears the data isn't there at all.
Maybe
SBTypeMember
needs anIsStatic()
function, or perhapsGetOffsetInBytes()
could return a sentinel value to indicate that the member does not have this pointer offset?Here's what the "offending" code looks like:
The text was updated successfully, but these errors were encountered: