Skip to content
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

Add an environment loader API and implementation for some DMI related fields #1063

Merged
merged 19 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ if (WIN32)

file(GLOB AWS_COMMON_OS_SRC
"source/windows/*.c"
"source/platform_fallback_stubs/system_info.c"
)

if (MSVC)
Expand Down Expand Up @@ -108,19 +109,26 @@ else ()
# Don't add the exact path to CoreFoundation as this would hardcode the SDK version
list(APPEND PLATFORM_LIBS dl Threads::Threads "-framework CoreFoundation")
list (APPEND AWS_COMMON_OS_SRC "source/darwin/*.c") # OS specific includes
list (APPEND AWS_COMMON_OS_SRC "source/platform_fallback_stubs/system_info.c")
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") # Android does not link to libpthread nor librt, so this is fine
list(APPEND PLATFORM_LIBS dl m Threads::Threads rt)
list (APPEND AWS_COMMON_OS_SRC "source/linux/*.c") # OS specific includes
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
list(APPEND PLATFORM_LIBS dl m thr execinfo)
list (APPEND AWS_COMMON_OS_SRC "source/platform_fallback_stubs/system_info.c")
elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
list(APPEND PLATFORM_LIBS dl m Threads::Threads execinfo)
list (APPEND AWS_COMMON_OS_SRC "source/platform_fallback_stubs/system_info.c")
elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
list(APPEND PLATFORM_LIBS m Threads::Threads execinfo)
list (APPEND AWS_COMMON_OS_SRC "source/platform_fallback_stubs/system_info.c")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
list(APPEND PLATFORM_LIBS log)
file(GLOB ANDROID_SRC "source/android/*.c")
list(APPEND AWS_COMMON_OS_SRC "${ANDROID_SRC}")
list (APPEND AWS_COMMON_OS_SRC "source/platform_fallback_stubs/system_info.c")
else()
list (APPEND AWS_COMMON_OS_SRC "source/platform_fallback_stubs/system_info.c")
endif()

endif()
Expand Down Expand Up @@ -298,6 +306,7 @@ configure_file(${CONFIG_HEADER_TEMPLATE}
if (ALLOW_CROSS_COMPILED_TESTS OR NOT CMAKE_CROSSCOMPILING)
if (BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(bin/system_info)
endif()
endif()

Expand Down
18 changes: 18 additions & 0 deletions bin/system_info/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
project(print-sys-info C)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/lib/cmake")

file(GLOB SI_SRC
"*.c"
)

set(SI_PROJECT_NAME print-sys-info)
add_executable(${SI_PROJECT_NAME} ${SI_SRC})
aws_set_common_properties(${SI_PROJECT_NAME})


target_include_directories(${SI_PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

target_link_libraries(${SI_PROJECT_NAME} PRIVATE aws-c-common)
48 changes: 48 additions & 0 deletions bin/system_info/print_system_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@


#include <aws/common/byte_buf.h>
#include <aws/common/logging.h>
#include <aws/common/system_info.h>

int main(void) {
struct aws_allocator *allocator = aws_default_allocator();
aws_common_library_init(allocator);
struct aws_logger_standard_options options = {
.file = stderr,
.level = AWS_LOG_LEVEL_TRACE,
};

struct aws_logger logger;
aws_logger_init_standard(&logger, allocator, &options);
aws_logger_set(&logger);

struct aws_system_environment *env = aws_system_environment_load(allocator);

fprintf(stdout, "crt-detected env: {\n");

struct aws_byte_cursor virtualization_vendor = aws_system_environment_get_virtualization_vendor(env);
fprintf(
stdout,
" 'virtualization vendor': '" PRInSTR "',\n",
(int)virtualization_vendor.len,
virtualization_vendor.ptr);
struct aws_byte_cursor product_name = aws_system_environment_get_virtualization_product_name(env);
fprintf(stdout, " 'product name': '" PRInSTR "',\n", (int)product_name.len, product_name.ptr);
fprintf(
stdout, " 'number of processors': '%lu',\n", (unsigned long)aws_system_environment_get_processor_count(env));
size_t numa_nodes = aws_system_environment_get_cpu_group_count(env);

if (numa_nodes > 1) {
fprintf(stdout, " 'numa architecture': 'true',\n");
fprintf(stdout, " 'number of numa nodes': '%lu'\n", (unsigned long)numa_nodes);
} else {
fprintf(stdout, " 'numa architecture': 'false'\n");
}

fprintf(stdout, "}\n");
aws_system_environment_release(env);
aws_logger_clean_up(&logger);

aws_common_library_clean_up();
return 0;
}
37 changes: 37 additions & 0 deletions include/aws/common/private/system_info_priv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef AWS_COMMON_PRIVATE_SYSTEM_INFO_PRIV_H
#define AWS_COMMON_PRIVATE_SYSTEM_INFO_PRIV_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/byte_buf.h>
#include <aws/common/ref_count.h>
#include <aws/common/string.h>
#include <aws/common/system_info.h>

struct aws_system_environment {
struct aws_allocator *allocator;
struct aws_ref_count ref_count;
struct aws_byte_buf virtualization_vendor;
struct aws_byte_buf product_name;
graebm marked this conversation as resolved.
Show resolved Hide resolved
enum aws_platform_os os;
size_t cpu_count;
size_t cpu_group_count;
void *impl;
};

/**
* For internal implementors. Fill in info in env that you're able to grab, such as dmi info, os version strings etc...
* in here. The default just returns AWS_OP_SUCCESS. This is currently only implemented for linux.
*
* Returns AWS_OP_ERR if the implementation wasn't able to fill in required information for the platform.
*/
int aws_system_environment_load_platform_impl(struct aws_system_environment *env);

/**
* For internal implementors. Cleans up anything allocated in aws_system_environment_load_platform_impl,
* but does not release the memory for env.
*/
void aws_system_environment_destroy_platform_impl(struct aws_system_environment *env);

#endif // AWS_COMMON_PRIVATE_SYSTEM_INFO_PRIV_H
47 changes: 47 additions & 0 deletions include/aws/common/system_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/common/byte_buf.h>
#include <aws/common/common.h>

AWS_PUSH_SANE_WARNING_LEVEL
Expand All @@ -21,8 +22,54 @@ struct aws_cpu_info {
bool suspected_hyper_thread;
};

struct aws_system_environment;

AWS_EXTERN_C_BEGIN

/**
* Allocates and initializes information about the system the current process is executing on.
* If successful returns an instance of aws_system_environment. If it fails, it will return NULL.
*
* Note: This api is used internally and is still early in its evolution.
* It may change in incompatible ways in the future.
*/
AWS_COMMON_API
struct aws_system_environment *aws_system_environment_load(struct aws_allocator *allocator);
graebm marked this conversation as resolved.
Show resolved Hide resolved

AWS_COMMON_API
struct aws_system_environment *aws_system_environment_acquire(struct aws_system_environment *env);

AWS_COMMON_API
void aws_system_environment_release(struct aws_system_environment *env);

/**
* Returns the virtualization vendor for the specified compute environment, e.g. "Xen, Amazon EC2, etc..."
*
* The return value may be empty and in that case no vendor was detected.
*/
AWS_COMMON_API
struct aws_byte_cursor aws_system_environment_get_virtualization_vendor(const struct aws_system_environment *env);
graebm marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the product name for the specified compute environment. For example, the Amazon EC2 Instance type.
*
* The return value may be empty and in that case no vendor was detected.
*/
AWS_COMMON_API
struct aws_byte_cursor aws_system_environment_get_virtualization_product_name(const struct aws_system_environment *env);

/**
* Returns the number of processors for the specified compute environment.
*/
AWS_COMMON_API
size_t aws_system_environment_get_processor_count(struct aws_system_environment *env);

/**
* Returns the number of separate cpu groupings (multi-socket configurations or NUMA).
*/
AWS_COMMON_API
size_t aws_system_environment_get_cpu_group_count(const struct aws_system_environment *env);

/* Returns the OS this was built under */
AWS_COMMON_API
enum aws_platform_os aws_get_platform_build_os(void);
Expand Down
24 changes: 24 additions & 0 deletions source/linux/system_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/file.h>
#include <aws/common/private/system_info_priv.h>

int aws_system_environment_load_platform_impl(struct aws_system_environment *env) {
/* provide size_hint when reading "special files", since some platforms mis-report these files' size as 4KB */
aws_byte_buf_init_from_file_with_size_hint(
&env->virtualization_vendor, env->allocator, "/sys/devices/virtual/dmi/id/sys_vendor", 32 /*size_hint*/);

/* whether this one works depends on if this is a sysfs filesystem. If it fails, it will just be empty
* and these APIs are a best effort at the moment. We can add fallbacks as the loaders get more complicated. */
aws_byte_buf_init_from_file_with_size_hint(
&env->product_name, env->allocator, "/sys/devices/virtual/dmi/id/product_name", 32 /*size_hint*/);

return AWS_OP_SUCCESS;
}

void aws_system_environment_destroy_platform_impl(struct aws_system_environment *env) {
aws_byte_buf_clean_up(&env->virtualization_vendor);
aws_byte_buf_clean_up(&env->product_name);
}
21 changes: 21 additions & 0 deletions source/platform_fallback_stubs/system_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/private/system_info_priv.h>

#include <aws/common/logging.h>

int aws_system_environment_load_platform_impl(struct aws_system_environment *env) {
(void)env;
AWS_LOGF_DEBUG(
AWS_LS_COMMON_GENERAL,
"id=%p: platform specific environment loading is not implemented for this platform.",
(void *)env);

return AWS_OP_SUCCESS;
}

void aws_system_environment_destroy_platform_impl(struct aws_system_environment *env) {
(void)env;
}
80 changes: 80 additions & 0 deletions source/system_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/private/system_info_priv.h>

#include <aws/common/logging.h>

void s_destroy_env(void *arg) {
struct aws_system_environment *env = arg;

if (env) {
aws_system_environment_destroy_platform_impl(env);
aws_mem_release(env->allocator, env);
}
}

struct aws_system_environment *aws_system_environment_load(struct aws_allocator *allocator) {
struct aws_system_environment *env = aws_mem_calloc(allocator, 1, sizeof(struct aws_system_environment));
env->allocator = allocator;
aws_ref_count_init(&env->ref_count, env, s_destroy_env);

if (aws_system_environment_load_platform_impl(env)) {
AWS_LOGF_ERROR(
AWS_LS_COMMON_GENERAL,
"id=%p: failed to load system environment with error %s.",
(void *)env,
aws_error_debug_str(aws_last_error()));
goto error;
}

AWS_LOGF_TRACE(
AWS_LS_COMMON_GENERAL,
"id=%p: virtualization vendor detected as \"" PRInSTR "\"",
(void *)env,
AWS_BYTE_CURSOR_PRI(aws_system_environment_get_virtualization_vendor(env)));
AWS_LOGF_TRACE(
AWS_LS_COMMON_GENERAL,
"id=%p: virtualization product name detected as \"" PRInSTR " \"",
(void *)env,
AWS_BYTE_CURSOR_PRI(aws_system_environment_get_virtualization_vendor(env)));

env->os = aws_get_platform_build_os();
env->cpu_count = aws_system_info_processor_count();
env->cpu_group_count = aws_get_cpu_group_count();

return env;
error:
s_destroy_env(env);
return NULL;
}

struct aws_system_environment *aws_system_environment_acquire(struct aws_system_environment *env) {
aws_ref_count_acquire(&env->ref_count);
return env;
}

void aws_system_environment_release(struct aws_system_environment *env) {
aws_ref_count_release(&env->ref_count);
}

struct aws_byte_cursor aws_system_environment_get_virtualization_vendor(const struct aws_system_environment *env) {
struct aws_byte_cursor vendor_string = aws_byte_cursor_from_buf(&env->virtualization_vendor);
return aws_byte_cursor_trim_pred(&vendor_string, aws_char_is_space);
}

struct aws_byte_cursor aws_system_environment_get_virtualization_product_name(
const struct aws_system_environment *env) {
struct aws_byte_cursor product_name_str = aws_byte_cursor_from_buf(&env->product_name);
return aws_byte_cursor_trim_pred(&product_name_str, aws_char_is_space);
}

size_t aws_system_environment_get_processor_count(struct aws_system_environment *env) {
return env->cpu_count;
}

AWS_COMMON_API
size_t aws_system_environment_get_cpu_group_count(const struct aws_system_environment *env) {
return env->cpu_group_count;
}
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ add_test_case(test_cpu_count_at_least_works_superficially)
add_test_case(test_stack_trace_decoding)
add_test_case(test_platform_build_os)
add_test_case(test_sanity_check_numa_discovery)
add_test_case(test_sanity_check_environment_loader)

add_test_case(test_realloc_fallback)
add_test_case(test_realloc_passthrough)
Expand Down
19 changes: 19 additions & 0 deletions tests/system_info_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,22 @@ static int s_test_sanity_check_numa_discovery(struct aws_allocator *allocator, v
}

AWS_TEST_CASE(test_sanity_check_numa_discovery, s_test_sanity_check_numa_discovery)

static int s_test_sanity_check_environment_loader(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

aws_common_library_init(allocator);
struct aws_system_environment *env = aws_system_environment_load(allocator);
ASSERT_NOT_NULL(env);
struct aws_byte_cursor virt_vendor = aws_system_environment_get_virtualization_vendor(env);
ASSERT_TRUE(aws_byte_cursor_is_valid(&virt_vendor));
struct aws_byte_cursor virt_product = aws_system_environment_get_virtualization_product_name(env);
ASSERT_TRUE(aws_byte_cursor_is_valid(&virt_product));

aws_system_environment_release(env);

aws_common_library_clean_up();
return 0;
}

AWS_TEST_CASE(test_sanity_check_environment_loader, s_test_sanity_check_environment_loader)
Loading