diff --git a/Meta/build-root-filesystem.sh b/Meta/build-root-filesystem.sh index 34e2f051f08..38c51528503 100755 --- a/Meta/build-root-filesystem.sh +++ b/Meta/build-root-filesystem.sh @@ -110,6 +110,7 @@ cp "$SERENITY_SOURCE_DIR"/README.md mnt/home/anon/ cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibJS/Tests mnt/home/anon/js-tests cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibWeb/Tests mnt/home/anon/web-tests cp -r "$SERENITY_SOURCE_DIR"/Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests mnt/home/anon/cpp-tests/comprehension +cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCpp/Tests mnt/home/anon/cpp-tests/parser chmod 700 mnt/root chmod 700 mnt/home/anon chmod 700 mnt/home/nona diff --git a/Meta/check-style.sh b/Meta/check-style.sh index b64c4c86961..f8e72c83d2f 100755 --- a/Meta/check-style.sh +++ b/Meta/check-style.sh @@ -12,7 +12,7 @@ cd "$script_path/.." || exit 1 # */ GOOD_LICENSE_HEADER_PATTERN=$'^/\*\n( \* Copyright \(c\) [0-9]{4}(-[0-9]{4})?, .*\n)+ \*\n \* SPDX-License-Identifier: BSD-2-Clause\n \*/\n\n' BAD_LICENSE_HEADER_ERRORS=() -LICENSE_HEADER_CHECK_EXCLUDES=(AK/Checked.h AK/Function.h Userland/Libraries/LibC/elf.h Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/*) +LICENSE_HEADER_CHECK_EXCLUDES=(AK/Checked.h AK/Function.h Userland/Libraries/LibC/elf.h Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/* Userland/Libraries/LibCpp/Tests/*) # We check that "#pragma once" is present PRAGMA_ONCE_PATTERN='#pragma once' diff --git a/Meta/lint-clang-format.sh b/Meta/lint-clang-format.sh index 7542a776951..3f83ef7c38c 100755 --- a/Meta/lint-clang-format.sh +++ b/Meta/lint-clang-format.sh @@ -13,7 +13,8 @@ if [ "$#" -eq "1" ]; then ':!:Base' \ ':!:Kernel/FileSystem/ext2_fs.h' \ ':!:Userland/Libraries/LibC/syslog.h' \ - ':!:Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/*' + ':!:Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/*' \ + ':!:Userland/Libraries/LibCpp/Tests/*' ) else files=() diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 6de315454e8..514bfb1a687 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,8 +1,9 @@ add_subdirectory(AK) add_subdirectory(Kernel) add_subdirectory(LibC) -add_subdirectory(LibCore) add_subdirectory(LibCompress) +add_subdirectory(LibCore) +add_subdirectory(LibCpp) add_subdirectory(LibELF) add_subdirectory(LibGfx) add_subdirectory(LibJS) diff --git a/Tests/LibCpp/CMakeLists.txt b/Tests/LibCpp/CMakeLists.txt new file mode 100644 index 00000000000..cbcd3b74882 --- /dev/null +++ b/Tests/LibCpp/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp") + +foreach(CMD_SRC ${CMD_SOURCES}) + serenity_test(${CMD_SRC} LibCpp LIBS LibCpp) +endforeach() diff --git a/Tests/LibCpp/test-cpp-parser.cpp b/Tests/LibCpp/test-cpp-parser.cpp new file mode 100644 index 00000000000..907818ea4a8 --- /dev/null +++ b/Tests/LibCpp/test-cpp-parser.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021, Itamar S. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr char TESTS_ROOT_DIR[] = "/home/anon/cpp-tests/parser"; + +static String read_all(const String& path) +{ + auto result = Core::File::open(path, Core::OpenMode::ReadOnly); + VERIFY(!result.is_error()); + auto content = result.value()->read_all(); + return { reinterpret_cast(content.data()), content.size() }; +} + +TEST_CASE(test_regression) +{ + Core::DirIterator directory_iterator(TESTS_ROOT_DIR, Core::DirIterator::Flags::SkipDots); + + while (directory_iterator.has_next()) { + auto file_path = directory_iterator.next_full_path(); + + auto path = LexicalPath { file_path }; + if (!path.has_extension(".cpp")) + continue; + + outln("Checking {}...", path.basename()); + + auto ast_file_path = String::formatted("{}.ast", file_path.substring(0, file_path.length() - sizeof(".cpp") + 1)); + + auto source = read_all(file_path); + auto target_ast = read_all(ast_file_path); + + StringView source_view(source); + ::Cpp::Parser parser(source_view, file_path); + auto root = parser.parse(); + + EXPECT(parser.errors().is_empty()); + + int pipefd[2] = {}; + if (pipe(pipefd) < 0) { + perror("pipe"); + exit(1); + } + + FILE* input_stream = fdopen(pipefd[0], "r"); + FILE* output_stream = fdopen(pipefd[1], "w"); + + root->dump(output_stream); + + fclose(output_stream); + + ByteBuffer buffer; + while (!feof(input_stream)) { + char chunk[4096]; + size_t size = fread(chunk, sizeof(char), sizeof(chunk), input_stream); + if (size == 0) + break; + buffer.append(chunk, size); + } + + fclose(input_stream); + + String content { reinterpret_cast(buffer.data()), buffer.size() }; + + auto equal = content == target_ast; + EXPECT(equal); + } +} diff --git a/Userland/Libraries/LibCpp/Tests/function-decl.ast b/Userland/Libraries/LibCpp/Tests/function-decl.ast new file mode 100644 index 00000000000..0f7f9b81db1 --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/function-decl.ast @@ -0,0 +1,10 @@ +TranslationUnit[0:0->2:0] + FunctionDeclaration[0:0->2:0] + Type[0:0->0:4] + int + foo + ( + ) + FunctionDefinition[1:0->2:0] + { + } diff --git a/Userland/Libraries/LibCpp/Tests/function-decl.cpp b/Userland/Libraries/LibCpp/Tests/function-decl.cpp new file mode 100644 index 00000000000..1c646d6c17c --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/function-decl.cpp @@ -0,0 +1,3 @@ +int foo() +{ +} diff --git a/Userland/Libraries/LibCpp/Tests/if-else.ast b/Userland/Libraries/LibCpp/Tests/if-else.ast new file mode 100644 index 00000000000..b50c8c0a608 --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/if-else.ast @@ -0,0 +1,33 @@ +TranslationUnit[0:0->7:0] + FunctionDeclaration[0:0->7:0] + Type[0:0->0:4] + int + foo + ( + ) + FunctionDefinition[1:0->7:0] + { + IfStatement[2:4->3:16] + Predicate: + NumericLiteral[2:8->2:8] + 2 + Then: + ReturnStatement[3:8->3:16] + NumericLiteral[3:15->3:15] + 2 + IfStatement[4:4->5:16] + Predicate: + BinaryExpression[4:8->4:12] + NumericLiteral[4:8->4:8] + 1 + < + NumericLiteral[4:12->4:12] + 2 + Then: + ReturnStatement[5:8->5:16] + NumericLiteral[5:15->5:15] + 1 + ReturnStatement[6:4->6:12] + NumericLiteral[6:11->6:11] + 0 + } diff --git a/Userland/Libraries/LibCpp/Tests/if-else.cpp b/Userland/Libraries/LibCpp/Tests/if-else.cpp new file mode 100644 index 00000000000..91d0fe62feb --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/if-else.cpp @@ -0,0 +1,8 @@ +int foo() +{ + if (2) + return 2; + if (1 < 2) + return 1; + return 0; +} diff --git a/Userland/Libraries/LibCpp/Tests/local-vars.ast b/Userland/Libraries/LibCpp/Tests/local-vars.ast new file mode 100644 index 00000000000..f1077569488 --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/local-vars.ast @@ -0,0 +1,33 @@ +TranslationUnit[1:0->7:0] + FunctionDeclaration[1:0->7:0] + Type[1:0->1:4] + int + foo + ( + ) + FunctionDefinition[2:0->7:0] + { + VariableDeclaration[3:4->3:9] + Type[3:4->3:8] + int + x + VariableDeclaration[4:4->4:16] + Type[4:4->4:11] + double + y + NumericLiteral[4:15->4:15] + 2 + VariableDeclaration[5:4->5:20] + Type[5:4->5:11] + double + z + BinaryExpression[5:15->5:20] + Name[5:15->5:17] + x + + + Name[5:19->5:20] + y + ReturnStatement[6:4->6:12] + Name[6:11->6:12] + z + } diff --git a/Userland/Libraries/LibCpp/Tests/local-vars.cpp b/Userland/Libraries/LibCpp/Tests/local-vars.cpp new file mode 100644 index 00000000000..42a2c3d235a --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/local-vars.cpp @@ -0,0 +1,8 @@ + +int foo() +{ + int x; + double y = 2; + double z = x + y; + return z; +} diff --git a/Userland/Libraries/LibCpp/Tests/strace.ast b/Userland/Libraries/LibCpp/Tests/strace.ast new file mode 100644 index 00000000000..8a01c830580 --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/strace.ast @@ -0,0 +1,879 @@ +TranslationUnit[0:0->144:0] + VariableDeclaration[0:0->2:0] + Type[0:0->0:11] + [static] int + g_pid + UnaryExpression[0:19->0:20] + - + NumericLiteral[0:20->0:20] + 1 + FunctionDeclaration[2:0->10:0] + [static] + Type[2:7->2:12] + void + handle_sigint + ( + Parameter[2:26->2:29] + Type[2:26->2:29] + int + ) + FunctionDefinition[3:0->10:0] + { + IfStatement[4:4->5:14] + Predicate: + BinaryExpression[4:8->4:18] + Name[4:8->4:14] + g_pid + == + UnaryExpression[4:17->4:18] + - + NumericLiteral[4:18->4:18] + 1 + Then: + ReturnStatement[5:8->5:14] + IfStatement[7:4->10:0] + Predicate: + BinaryExpression[7:8->7:43] + FunctionCall[7:8->7:39] + Name[7:8->7:14] + ptrace + Name[7:15->7:24] + PT_DETACH + Name[7:26->7:31] + g_pid + NumericLiteral[7:33->7:33] + 0 + NumericLiteral[7:36->7:36] + 0 + == + UnaryExpression[7:42->7:43] + - + NumericLiteral[7:43->7:43] + 1 + Then: + BlockStatement[7:46->10:0] + FunctionCall[8:8->8:24] + Name[8:8->8:14] + perror + StringLiteral[8:15->8:22] + "detach" + } + FunctionDeclaration[12:0->144:0] + Type[12:0->12:4] + int + main + ( + Parameter[12:9->12:16] + argc + Type[12:9->12:13] + int + Parameter[12:24->12:29] + argv + Pointer[12:24->12:26] + Pointer[12:23->12:24] + Type[12:19->12:23] + char + ) + FunctionDefinition[13:0->144:0] + { + IfStatement[14:4->19:4] + Predicate: + BinaryExpression[14:8->14:74] + FunctionCall[14:8->14:72] + Name[14:8->14:14] + pledge + StringLiteral[14:15->14:60] + "stdio wpath cpath proc exec ptrace sigaction" + NullPointerLiteral[14:63->14:69] + < + NumericLiteral[14:74->14:74] + 0 + Then: + BlockStatement[14:77->19:4] + FunctionCall[15:8->15:24] + Name[15:8->15:14] + perror + StringLiteral[15:15->15:22] + "pledge" + ReturnStatement[16:8->16:16] + NumericLiteral[16:15->16:15] + 1 + VariableDeclaration[19:4->19:34] + Type[19:4->19:24] + Vector<[const] char*> + child_argv + VariableDeclaration[21:4->21:41] + Pointer[21:14->21:16] + Type[21:4->21:14] + [const] char + output_filename + NullPointerLiteral[21:34->21:40] + VariableDeclaration[22:4->22:50] + Type[22:4->22:9] + auto + trace_file + FunctionCall[22:22->22:50] + Name[22:22->22:48] + Core::File::standard_error + VariableDeclaration[24:4->24:27] + Type[24:4->24:21] + Core::ArgsParser + parser + FunctionCall[25:4->26:47] + MemberExpression[25:4->25:27] + Name[25:4->25:10] + parser + Identifier[25:11->25:26] + set_general_help + StringLiteral[26:8->26:45] + "Trace all syscalls and their result." + FunctionCall[27:4->27:70] + MemberExpression[27:4->27:21] + Name[27:4->27:10] + parser + Identifier[27:11->27:20] + add_option + Name[27:22->27:27] + g_pid + StringLiteral[27:29->27:49] + "Trace the given PID" + StringLiteral[27:52->27:56] + "pid" + StringLiteral[27:59->27:61] + 'p' + StringLiteral[27:64->27:68] + "pid" + FunctionCall[28:4->28:94] + MemberExpression[28:4->28:21] + Name[28:4->28:10] + parser + Identifier[28:11->28:20] + add_option + Name[28:22->28:37] + output_filename + StringLiteral[28:39->28:67] + "Filename to write output to" + StringLiteral[28:70->28:77] + "output" + StringLiteral[28:80->28:82] + 'o' + StringLiteral[28:85->28:92] + "output" + FunctionCall[29:4->29:111] + MemberExpression[29:4->29:34] + Name[29:4->29:10] + parser + Identifier[29:11->29:33] + add_positional_argument + Name[29:35->29:45] + child_argv + StringLiteral[29:47->29:65] + "Arguments to exec" + StringLiteral[29:68->29:77] + "argument" + Name[29:80->29:110] + Core::ArgsParser::Required::No + FunctionCall[31:4->31:28] + MemberExpression[31:4->31:16] + Name[31:4->31:10] + parser + Identifier[31:11->31:15] + parse + Name[31:17->31:21] + argc + Name[31:23->31:27] + argv + IfStatement[33:4->42:4] + Predicate: + BinaryExpression[33:8->33:33] + Name[33:8->33:24] + output_filename + != + NullPointerLiteral[33:27->33:33] + Then: + BlockStatement[33:36->42:4] + VariableDeclaration[34:8->34:87] + Type[34:8->34:13] + auto + open_result + FunctionCall[34:27->34:87] + Name[34:27->34:43] + Core::File::open + Name[34:44->34:59] + output_filename + Name[34:61->34:86] + Core::OpenMode::WriteOnly + IfStatement[35:8->39:8] + Predicate: + FunctionCall[35:12->35:34] + MemberExpression[35:12->35:32] + Name[35:12->35:23] + open_result + Identifier[35:24->35:31] + is_error + Then: + BlockStatement[35:36->39:8] + FunctionCall[36:12->36:80] + Name[36:12->36:17] + outln + Name[36:18->36:24] + stderr + StringLiteral[36:26->36:57] + "Failed to open output file: {}" + FunctionCall[36:60->36:79] + MemberExpression[36:60->36:77] + Name[36:60->36:71] + open_result + Identifier[36:72->36:76] + error + ReturnStatement[37:12->37:20] + NumericLiteral[37:19->37:19] + 1 + AssignmentExpression[39:8->39:40] + Name[39:8->39:19] + trace_file + = + FunctionCall[39:21->39:40] + MemberExpression[39:21->39:38] + Name[39:21->39:32] + open_result + Identifier[39:33->39:37] + value + IfStatement[42:4->47:4] + Predicate: + BinaryExpression[42:8->42:62] + FunctionCall[42:8->42:60] + Name[42:8->42:14] + pledge + StringLiteral[42:15->42:48] + "stdio proc exec ptrace sigaction" + NullPointerLiteral[42:51->42:57] + < + NumericLiteral[42:62->42:62] + 0 + Then: + BlockStatement[42:65->47:4] + FunctionCall[43:8->43:24] + Name[43:8->43:14] + perror + StringLiteral[43:15->43:22] + "pledge" + ReturnStatement[44:8->44:16] + NumericLiteral[44:15->44:15] + 1 + VariableDeclaration[47:4->47:14] + Type[47:4->47:8] + int + status + IfStatement[48:4->81:4] + Predicate: + BinaryExpression[48:8->48:18] + Name[48:8->48:14] + g_pid + == + UnaryExpression[48:17->48:18] + - + NumericLiteral[48:18->48:18] + 1 + Then: + BlockStatement[48:21->81:4] + IfStatement[49:8->54:8] + Predicate: + FunctionCall[49:12->49:33] + MemberExpression[49:12->49:31] + Name[49:12->49:22] + child_argv + Identifier[49:23->49:30] + is_empty + Then: + BlockStatement[49:35->54:8] + FunctionCall[50:12->50:78] + Name[50:12->50:17] + outln + Name[50:18->50:24] + stderr + StringLiteral[50:26->50:76] + "strace: Expected either a pid or some arguments\n" + ReturnStatement[51:12->51:20] + NumericLiteral[51:19->51:19] + 1 + FunctionCall[54:8->54:34] + MemberExpression[54:8->54:25] + Name[54:8->54:18] + child_argv + Identifier[54:19->54:24] + append + NullPointerLiteral[54:26->54:32] + VariableDeclaration[55:8->55:24] + Type[55:8->55:12] + int + pid + FunctionCall[55:18->55:24] + Name[55:18->55:22] + fork + IfStatement[56:8->61:8] + Predicate: + BinaryExpression[56:12->56:18] + Name[56:12->56:16] + pid + < + NumericLiteral[56:18->56:18] + 0 + Then: + BlockStatement[56:21->61:8] + FunctionCall[57:12->57:26] + Name[57:12->57:18] + perror + StringLiteral[57:19->57:24] + "fork" + ReturnStatement[58:12->58:20] + NumericLiteral[58:19->58:19] + 1 + IfStatement[61:8->74:8] + Predicate: + UnaryExpression[61:12->61:16] + ! + Name[61:13->61:16] + pid + Then: + BlockStatement[61:18->74:8] + IfStatement[62:12->66:12] + Predicate: + BinaryExpression[62:16->62:49] + FunctionCall[62:16->62:45] + Name[62:16->62:22] + ptrace + Name[62:23->62:34] + PT_TRACE_ME + NumericLiteral[62:36->62:36] + 0 + NumericLiteral[62:39->62:39] + 0 + NumericLiteral[62:42->62:42] + 0 + == + UnaryExpression[62:48->62:49] + - + NumericLiteral[62:49->62:49] + 1 + Then: + BlockStatement[62:52->66:12] + FunctionCall[63:16->63:33] + Name[63:16->63:22] + perror + StringLiteral[63:23->63:31] + "traceme" + ReturnStatement[64:16->64:24] + NumericLiteral[64:23->64:23] + 1 + VariableDeclaration[66:12->66:86] + Type[66:12->66:16] + int + rc + FunctionCall[66:21->66:86] + Name[66:21->66:27] + execvp + FunctionCall[66:28->66:46] + MemberExpression[66:28->66:44] + Name[66:28->66:38] + child_argv + Identifier[66:39->66:43] + first + CppCastExpression[66:48->66:85] + const_cast + < + Pointer[66:64->66:65] + Pointer[66:63->66:64] + Type[66:59->66:63] + char + > + FunctionCall[66:67->66:84] + MemberExpression[66:67->66:82] + Name[66:67->66:77] + child_argv + Identifier[66:78->66:81] + data + IfStatement[67:12->71:12] + Predicate: + BinaryExpression[67:16->67:21] + Name[67:16->67:19] + rc + < + NumericLiteral[67:21->67:21] + 0 + Then: + BlockStatement[67:24->71:12] + FunctionCall[68:16->68:32] + Name[68:16->68:22] + perror + StringLiteral[68:23->68:30] + "execvp" + FunctionCall[69:16->69:23] + Name[69:16->69:20] + exit + NumericLiteral[69:21->69:21] + 1 + FunctionCall[71:12->71:32] + Name[71:12->71:30] + VERIFY_NOT_REACHED + AssignmentExpression[74:8->74:19] + Name[74:8->74:14] + g_pid + = + Name[74:16->74:19] + pid + IfStatement[75:8->79:4] + Predicate: + BinaryExpression[75:12->75:83] + FunctionCall[75:12->75:54] + Name[75:12->75:19] + waitpid + Name[75:20->75:23] + pid + UnaryExpression[75:25->75:32] + & + Name[75:26->75:32] + status + BinaryExpression[75:34->75:52] + Name[75:34->75:43] + WSTOPPED + | + Name[75:45->75:52] + WEXITED + != + BinaryExpression[75:57->75:83] + Name[75:57->75:61] + pid + || + UnaryExpression[75:64->75:83] + ! + FunctionCall[75:65->75:83] + Name[75:65->75:75] + WIFSTOPPED + Name[75:76->75:82] + status + Then: + BlockStatement[75:85->79:4] + FunctionCall[76:12->76:29] + Name[76:12->76:18] + perror + StringLiteral[76:19->76:27] + "waitpid" + ReturnStatement[77:12->77:20] + NumericLiteral[77:19->77:19] + 1 + VariableDeclaration[81:4->81:23] + Type[81:4->81:21] + sigaction + sa + FunctionCall[82:4->82:44] + Name[82:4->82:10] + memset + UnaryExpression[82:11->82:14] + & + Name[82:12->82:14] + sa + NumericLiteral[82:16->82:16] + 0 + SizeofExpression[82:19->82:43] + Type[82:26->82:42] + sigaction + AssignmentExpression[83:4->83:33] + MemberExpression[83:4->83:18] + Name[83:4->83:6] + sa + Identifier[83:7->83:16] + sa_handler + = + Name[83:20->83:33] + handle_sigint + FunctionCall[84:4->84:35] + Name[84:4->84:13] + sigaction + Name[84:14->84:20] + SIGINT + UnaryExpression[84:22->84:25] + & + Name[84:23->84:25] + sa + NullPointerLiteral[84:27->84:33] + IfStatement[86:4->90:4] + Predicate: + BinaryExpression[86:8->86:43] + FunctionCall[86:8->86:39] + Name[86:8->86:14] + ptrace + Name[86:15->86:24] + PT_ATTACH + Name[86:26->86:31] + g_pid + NumericLiteral[86:33->86:33] + 0 + NumericLiteral[86:36->86:36] + 0 + == + UnaryExpression[86:42->86:43] + - + NumericLiteral[86:43->86:43] + 1 + Then: + BlockStatement[86:46->90:4] + FunctionCall[87:8->87:24] + Name[87:8->87:14] + perror + StringLiteral[87:15->87:22] + "attach" + ReturnStatement[88:8->88:16] + NumericLiteral[88:15->88:15] + 1 + IfStatement[90:4->95:4] + Predicate: + BinaryExpression[90:8->90:83] + FunctionCall[90:8->90:52] + Name[90:8->90:15] + waitpid + Name[90:16->90:21] + g_pid + UnaryExpression[90:23->90:30] + & + Name[90:24->90:30] + status + BinaryExpression[90:32->90:50] + Name[90:32->90:41] + WSTOPPED + | + Name[90:43->90:50] + WEXITED + != + BinaryExpression[90:55->90:83] + Name[90:55->90:61] + g_pid + || + UnaryExpression[90:64->90:83] + ! + FunctionCall[90:65->90:83] + Name[90:65->90:75] + WIFSTOPPED + Name[90:76->90:82] + status + Then: + BlockStatement[90:85->95:4] + FunctionCall[91:8->91:25] + Name[91:8->91:14] + perror + StringLiteral[91:15->91:23] + "waitpid" + ReturnStatement[92:8->92:16] + NumericLiteral[92:15->92:15] + 1 + ForStatement[95:4->143:4] + BlockStatement[95:13->143:4] + IfStatement[96:8->100:8] + Predicate: + BinaryExpression[96:12->96:48] + FunctionCall[96:12->96:44] + Name[96:12->96:18] + ptrace + Name[96:19->96:29] + PT_SYSCALL + Name[96:31->96:36] + g_pid + NumericLiteral[96:38->96:38] + 0 + NumericLiteral[96:41->96:41] + 0 + == + UnaryExpression[96:47->96:48] + - + NumericLiteral[96:48->96:48] + 1 + Then: + BlockStatement[96:51->100:8] + FunctionCall[97:12->97:29] + Name[97:12->97:18] + perror + StringLiteral[97:19->97:27] + "syscall" + ReturnStatement[98:12->98:20] + NumericLiteral[98:19->98:19] + 1 + IfStatement[100:8->104:8] + Predicate: + BinaryExpression[100:12->100:87] + FunctionCall[100:12->100:56] + Name[100:12->100:19] + waitpid + Name[100:20->100:25] + g_pid + UnaryExpression[100:27->100:34] + & + Name[100:28->100:34] + status + BinaryExpression[100:36->100:54] + Name[100:36->100:45] + WSTOPPED + | + Name[100:47->100:54] + WEXITED + != + BinaryExpression[100:59->100:87] + Name[100:59->100:65] + g_pid + || + UnaryExpression[100:68->100:87] + ! + FunctionCall[100:69->100:87] + Name[100:69->100:79] + WIFSTOPPED + Name[100:80->100:86] + status + Then: + BlockStatement[100:89->104:8] + FunctionCall[101:12->101:30] + Name[101:12->101:18] + perror + StringLiteral[101:19->101:28] + "wait_pid" + ReturnStatement[102:12->102:20] + NumericLiteral[102:19->102:19] + 1 + VariableDeclaration[104:8->104:33] + Type[104:8->104:24] + PtraceRegisters + regs + BracedInitList[104:31->104:33] + IfStatement[105:8->109:8] + Predicate: + BinaryExpression[105:12->105:52] + FunctionCall[105:12->105:48] + Name[105:12->105:18] + ptrace + Name[105:19->105:29] + PT_GETREGS + Name[105:31->105:36] + g_pid + UnaryExpression[105:38->105:43] + & + Name[105:39->105:43] + regs + NumericLiteral[105:45->105:45] + 0 + == + UnaryExpression[105:51->105:52] + - + NumericLiteral[105:52->105:52] + 1 + Then: + BlockStatement[105:55->109:8] + FunctionCall[106:12->106:29] + Name[106:12->106:18] + perror + StringLiteral[106:19->106:27] + "getregs" + ReturnStatement[107:12->107:20] + NumericLiteral[107:19->107:19] + 1 + VariableDeclaration[109:8->109:36] + Type[109:8->109:12] + u32 + syscall_index + MemberExpression[109:28->109:36] + Name[109:28->109:32] + regs + Identifier[109:33->109:35] + eax + VariableDeclaration[110:8->110:27] + Type[110:8->110:12] + u32 + arg1 + MemberExpression[110:19->110:27] + Name[110:19->110:23] + regs + Identifier[110:24->110:26] + edx + VariableDeclaration[111:8->111:27] + Type[111:8->111:12] + u32 + arg2 + MemberExpression[111:19->111:27] + Name[111:19->111:23] + regs + Identifier[111:24->111:26] + ecx + VariableDeclaration[112:8->112:27] + Type[112:8->112:12] + u32 + arg3 + MemberExpression[112:19->112:27] + Name[112:19->112:23] + regs + Identifier[112:24->112:26] + ebx + IfStatement[114:8->118:8] + Predicate: + BinaryExpression[114:12->114:48] + FunctionCall[114:12->114:44] + Name[114:12->114:18] + ptrace + Name[114:19->114:29] + PT_SYSCALL + Name[114:31->114:36] + g_pid + NumericLiteral[114:38->114:38] + 0 + NumericLiteral[114:41->114:41] + 0 + == + UnaryExpression[114:47->114:48] + - + NumericLiteral[114:48->114:48] + 1 + Then: + BlockStatement[114:51->118:8] + FunctionCall[115:12->115:29] + Name[115:12->115:18] + perror + StringLiteral[115:19->115:27] + "syscall" + ReturnStatement[116:12->116:20] + NumericLiteral[116:19->116:19] + 1 + IfStatement[118:8->123:8] + Predicate: + BinaryExpression[118:12->118:87] + FunctionCall[118:12->118:56] + Name[118:12->118:19] + waitpid + Name[118:20->118:25] + g_pid + UnaryExpression[118:27->118:34] + & + Name[118:28->118:34] + status + BinaryExpression[118:36->118:54] + Name[118:36->118:45] + WSTOPPED + | + Name[118:47->118:54] + WEXITED + != + BinaryExpression[118:59->118:87] + Name[118:59->118:65] + g_pid + || + UnaryExpression[118:68->118:87] + ! + FunctionCall[118:69->118:87] + Name[118:69->118:79] + WIFSTOPPED + Name[118:80->118:86] + status + Then: + BlockStatement[118:89->123:8] + FunctionCall[119:12->119:30] + Name[119:12->119:18] + perror + StringLiteral[119:19->119:28] + "wait_pid" + ReturnStatement[120:12->120:20] + NumericLiteral[120:19->120:19] + 1 + IfStatement[123:8->128:8] + Predicate: + BinaryExpression[123:12->123:52] + FunctionCall[123:12->123:48] + Name[123:12->123:18] + ptrace + Name[123:19->123:29] + PT_GETREGS + Name[123:31->123:36] + g_pid + UnaryExpression[123:38->123:43] + & + Name[123:39->123:43] + regs + NumericLiteral[123:45->123:45] + 0 + == + UnaryExpression[123:51->123:52] + - + NumericLiteral[123:52->123:52] + 1 + Then: + BlockStatement[123:55->128:8] + FunctionCall[124:12->124:29] + Name[124:12->124:18] + perror + StringLiteral[124:19->124:27] + "getregs" + ReturnStatement[125:12->125:20] + NumericLiteral[125:19->125:19] + 1 + VariableDeclaration[128:8->128:26] + Type[128:8->128:12] + u32 + res + MemberExpression[128:18->128:26] + Name[128:18->128:22] + regs + Identifier[128:23->128:25] + eax + VariableDeclaration[130:8->135:16] + Type[130:8->130:13] + auto + string + FunctionCall[130:22->135:16] + Name[130:22->130:39] + String::formatted + StringLiteral[130:40->130:77] + "{}({:#08x}, {:#08x}, {:#08x})\t={}\n" + FunctionCall[131:12->131:64] + Name[131:12->131:30] + Syscall::to_string + CStyleCastExpression[131:31->131:63] + Type[131:32->131:49] + Syscall::Function + Name[131:50->131:63] + syscall_index + Name[132:12->132:16] + arg1 + Name[133:12->133:16] + arg2 + Name[134:12->134:16] + arg3 + Name[135:12->135:15] + res + IfStatement[137:8->141:4] + Predicate: + UnaryExpression[137:12->137:38] + ! + BinaryExpression[137:13->137:38] + Name[137:13->137:23] + trace_file + -> + FunctionCall[137:25->137:38] + Name[137:25->137:30] + write + Name[137:31->137:37] + string + Then: + BlockStatement[137:40->141:4] + FunctionCall[138:12->138:59] + Name[138:12->138:18] + warnln + StringLiteral[138:19->138:29] + "write: {}" + BinaryExpression[138:32->138:58] + Name[138:32->138:42] + trace_file + -> + FunctionCall[138:44->138:58] + Name[138:44->138:56] + error_string + ReturnStatement[139:12->139:20] + NumericLiteral[139:19->139:19] + 1 + ReturnStatement[143:4->143:12] + NumericLiteral[143:11->143:11] + 0 + } diff --git a/Userland/Libraries/LibCpp/Tests/strace.cpp b/Userland/Libraries/LibCpp/Tests/strace.cpp new file mode 100644 index 00000000000..9844198f63c --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/strace.cpp @@ -0,0 +1,145 @@ +static int g_pid = -1; + +static void handle_sigint(int) +{ + if (g_pid == -1) + return; + + if (ptrace(PT_DETACH, g_pid, 0, 0) == -1) { + perror("detach"); + } +} + +int main(int argc, char** argv) +{ + if (pledge("stdio wpath cpath proc exec ptrace sigaction", nullptr) < 0) { + perror("pledge"); + return 1; + } + + Vector child_argv; + + const char* output_filename = nullptr; + auto trace_file = Core::File::standard_error(); + + Core::ArgsParser parser; + parser.set_general_help( + "Trace all syscalls and their result."); + parser.add_option(g_pid, "Trace the given PID", "pid", 'p', "pid"); + parser.add_option(output_filename, "Filename to write output to", "output", 'o', "output"); + parser.add_positional_argument(child_argv, "Arguments to exec", "argument", Core::ArgsParser::Required::No); + + parser.parse(argc, argv); + + if (output_filename != nullptr) { + auto open_result = Core::File::open(output_filename, Core::OpenMode::WriteOnly); + if (open_result.is_error()) { + outln(stderr, "Failed to open output file: {}", open_result.error()); + return 1; + } + trace_file = open_result.value(); + } + + if (pledge("stdio proc exec ptrace sigaction", nullptr) < 0) { + perror("pledge"); + return 1; + } + + int status; + if (g_pid == -1) { + if (child_argv.is_empty()) { + outln(stderr, "strace: Expected either a pid or some arguments\n"); + return 1; + } + + child_argv.append(nullptr); + int pid = fork(); + if (pid < 0) { + perror("fork"); + return 1; + } + + if (!pid) { + if (ptrace(PT_TRACE_ME, 0, 0, 0) == -1) { + perror("traceme"); + return 1; + } + int rc = execvp(child_argv.first(), const_cast(child_argv.data())); + if (rc < 0) { + perror("execvp"); + exit(1); + } + VERIFY_NOT_REACHED(); + } + + g_pid = pid; + if (waitpid(pid, &status, WSTOPPED | WEXITED) != pid || !WIFSTOPPED(status)) { + perror("waitpid"); + return 1; + } + } + + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = handle_sigint; + sigaction(SIGINT, &sa, nullptr); + + if (ptrace(PT_ATTACH, g_pid, 0, 0) == -1) { + perror("attach"); + return 1; + } + if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) { + perror("waitpid"); + return 1; + } + + for (;;) { + if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) { + perror("syscall"); + return 1; + } + if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) { + perror("wait_pid"); + return 1; + } + PtraceRegisters regs = {}; + if (ptrace(PT_GETREGS, g_pid, ®s, 0) == -1) { + perror("getregs"); + return 1; + } + u32 syscall_index = regs.eax; + u32 arg1 = regs.edx; + u32 arg2 = regs.ecx; + u32 arg3 = regs.ebx; + + if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) { + perror("syscall"); + return 1; + } + if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) { + perror("wait_pid"); + return 1; + } + + if (ptrace(PT_GETREGS, g_pid, ®s, 0) == -1) { + perror("getregs"); + return 1; + } + + u32 res = regs.eax; + + auto string = String::formatted("{}({:#08x}, {:#08x}, {:#08x})\t={}\n", + Syscall::to_string((Syscall::Function)syscall_index), + arg1, + arg2, + arg3, + res); + + if (!trace_file->write(string)) { + warnln("write: {}", trace_file->error_string()); + return 1; + } + } + + return 0; +} diff --git a/Userland/Libraries/LibCpp/Tests/struct.ast b/Userland/Libraries/LibCpp/Tests/struct.ast new file mode 100644 index 00000000000..437686b3446 --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/struct.ast @@ -0,0 +1,41 @@ +TranslationUnit[1:0->12:0] + StructOrClassDeclaration[1:0->7:0] + MyStruct + MemberDeclaration[3:4->4:4] + Type[3:4->3:8] + int + x + MemberDeclaration[4:4->5:0] + Pointer[4:12->4:14] + Type[4:4->4:12] + s + next + FunctionDeclaration[7:0->12:0] + Type[7:0->7:4] + int + foo + ( + ) + FunctionDefinition[8:0->12:0] + { + VariableDeclaration[9:4->9:14] + Type[9:4->9:13] + MyStruct + s + FunctionCall[10:4->10:23] + Name[10:4->10:10] + printf + StringLiteral[10:11->10:16] + "%d\n" + MemberExpression[10:19->10:22] + Name[10:19->10:20] + s + Identifier[10:21->10:21] + x + ReturnStatement[11:4->11:14] + MemberExpression[11:11->11:14] + Name[11:11->11:12] + s + Identifier[11:13->11:13] + x + } diff --git a/Userland/Libraries/LibCpp/Tests/struct.cpp b/Userland/Libraries/LibCpp/Tests/struct.cpp new file mode 100644 index 00000000000..4cc7953275e --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/struct.cpp @@ -0,0 +1,13 @@ + +struct MyStruct +{ + int x; + struct s* next; +}; + +int foo() +{ + MyStruct s; + printf("%d\n", s.x); + return s.x; +}