diff --git a/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py b/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py index 8676e88d9..7a7483a2f 100755 --- a/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py +++ b/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py @@ -8,6 +8,7 @@ "_Complex", "char", "const", + "class", "double", "enum", "float", diff --git a/libdrgn/language_c.c b/libdrgn/language_c.c index 6d6343fdf..572f79162 100644 --- a/libdrgn/language_c.c +++ b/libdrgn/language_c.c @@ -1643,6 +1643,7 @@ enum { MAX_QUALIFIER_TOKEN = C_TOKEN_ATOMIC, C_TOKEN_STRUCT, C_TOKEN_UNION, + C_TOKEN_CLASS, C_TOKEN_ENUM, MAX_KEYWORD_TOKEN = C_TOKEN_ENUM, C_TOKEN_LPAREN, @@ -2107,7 +2108,8 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, identifier_len = token.len; } else if (token.kind == C_TOKEN_STRUCT || token.kind == C_TOKEN_UNION || - token.kind == C_TOKEN_ENUM) { + token.kind == C_TOKEN_ENUM || + token.kind == C_TOKEN_CLASS) { if (identifier) { return drgn_error_format(DRGN_ERROR_SYNTAX, "cannot combine '%s' with identifier", @@ -2148,6 +2150,8 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, kind = DRGN_TYPE_UNION; } else if (tag_token == C_TOKEN_ENUM) { kind = DRGN_TYPE_ENUM; + } else if (tag_token == C_TOKEN_CLASS) { + kind = DRGN_TYPE_CLASS; } else if (identifier) { if (strstartswith(identifier, "size_t")) { err = drgn_program_find_primitive_type(prog, diff --git a/tests/libdrgn.py b/tests/libdrgn.py index dba4dbb14..5d0f62f74 100644 --- a/tests/libdrgn.py +++ b/tests/libdrgn.py @@ -175,6 +175,7 @@ class C_TOKEN(enum.IntEnum): ATOMIC = auto() STRUCT = auto() UNION = auto() + CLASS = auto() ENUM = auto() LPAREN = auto() RPAREN = auto() diff --git a/tests/sample.cpp.coredump.zst b/tests/sample.cpp.coredump.zst new file mode 100644 index 000000000..e34293298 Binary files /dev/null and b/tests/sample.cpp.coredump.zst differ diff --git a/tests/test_e2e_cpp.py b/tests/test_e2e_cpp.py new file mode 100644 index 000000000..44063a778 --- /dev/null +++ b/tests/test_e2e_cpp.py @@ -0,0 +1,59 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: GPL-3.0-or-later + +import os +import os.path +import subprocess +import tempfile +import unittest + +from drgn import Program, MissingDebugInfoError +from tests import TestCase + + +class TestE2ECPP(TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + with tempfile.NamedTemporaryFile() as core_dump_file: + try: + subprocess.check_call( + [ + "zstd", + "--quiet", + "--decompress", + "--stdout", + "tests/sample.cpp.coredump.zst", + ], + stdout=core_dump_file, + ) + except FileNotFoundError: + raise unittest.SkipTest("zstd not found") + cls.prog = Program() + cls.prog.set_core_dump(core_dump_file.name) + try: + cls.prog.load_default_debug_info() + except MissingDebugInfoError: + pass + + def test_struct_e2e(self): + self.assertSequenceEqual( + "struct S {\n" + "\tint def;\n" + "\tint pub;\n" + "\tint pri;\n" + "\tint prt;\n" + "}", + self.prog.type("struct S").__str__() + ) + + def test_class_e2e(self): + self.assertSequenceEqual( + "class A {\n" + "\tint def;\n" + "\tint pub;\n" + "\tint pri;\n" + "\tint prt;\n" + "}", + self.prog.type("class A").__str__() + )