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 os_type and os_version keys to erlang:system_info/1 #1117

Open
wants to merge 5 commits into
base: release-0.6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.3] - Unreleased

### Added
- Added support for `erlang:system_info/1` keys `os_type` and `os_version`.

## [0.6.2] - 25-05-2024

### Added
Expand Down
2 changes: 2 additions & 0 deletions doc/src/programmers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,8 @@ For more information about Erlang external term format, consult the [Erlang Docu

You can obtain system information about the AtomVM virtual machine via the [`erlang:system_info/1`](./apidocs/erlang/estdlib/erlang.md#system_info1) function, which takes an atom parameter designating the desired datum. Allowable parameters include

* `os_type` The operating system or runtime environment.
* `os_version` The sematic version of the currently running operating system or runtime environment.
* `process_count` The number of processes running in the system.
* `port_count` The number of ports running in the system.
* `atom_count` The number of atoms allocated in the system.
Expand Down
2 changes: 2 additions & 0 deletions libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ process_info(_Pid, _Key) ->
%%
%% The following keys are supported on all platforms:
%% <ul>
%% <li><b>os_type</b> tuple containing `{Osfamily :: atom(), Osname (or IDE) :: atom()}'</li>
%% <li><b>os_version</b> operating system sematic version `{Major :: integer(), Minor :: integer(), Patch :: integer()}'</li>
%% <li><b>process_count</b> the number of processes running in the node (integer)</li>
%% <li><b>port_count</b> the number of ports running in the node (integer)</li>
%% <li><b>atom_count</b> the number of atoms currently allocated (integer)</li>
Expand Down
24 changes: 18 additions & 6 deletions src/platforms/esp32/components/avm_sys/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "sys.h"
#include "esp32_sys.h"
#include "platform_defaultatoms.h"

#include "avmpack.h"
#include "defaultatoms.h"
Expand Down Expand Up @@ -78,7 +79,6 @@ static const char *const esp_free_heap_size_atom = "\x14" "esp32_free_heap_size"
static const char *const esp_largest_free_block_atom = "\x18" "esp32_largest_free_block";
static const char *const esp_get_minimum_free_size_atom = "\x17" "esp32_minimum_free_size";
static const char *const esp_chip_info_atom = "\xF" "esp32_chip_info";
static const char *const esp_idf_version_atom = "\xF" "esp_idf_version";
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 2)
static const char *const esp32_atom = "\x5" "esp32";
static const char *const esp32_s2_atom = "\x8" "esp32_s2";
Expand Down Expand Up @@ -543,13 +543,25 @@ term sys_get_info(Context *ctx, term key)
term_set_map_assoc(ret, 3, globalcontext_make_atom(glb, revision_atom), term_from_int32(info.revision));
return ret;
}
if (key == globalcontext_make_atom(glb, esp_idf_version_atom)) {
const char *str = esp_get_idf_version();
size_t n = strlen(str);
if (memory_ensure_free(ctx, 2 * n) != MEMORY_GC_OK) {
if (key == globalcontext_make_atom(glb, ATOM_STR("\x7", "os_type"))) {
if (memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
return term_from_string((const uint8_t *) str, n, &ctx->heap);
term os = term_alloc_tuple(2, &ctx->heap);
term_put_tuple_element(os, 0, ESP32_ATOM);
term_put_tuple_element(os, 1, globalcontext_make_atom(glb, ATOM_STR("\x7", "esp-idf")));

return os;
}
if (key == globalcontext_make_atom(glb, ATOM_STR("\xA", "os_version"))) {
if (memory_ensure_free(ctx, TUPLE_SIZE(3)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os_ver = term_alloc_tuple(3, &ctx->heap);
term_put_tuple_element(os_ver, 0, term_from_int32(ESP_IDF_VERSION_MAJOR));
term_put_tuple_element(os_ver, 1, term_from_int32(ESP_IDF_VERSION_MINOR));
term_put_tuple_element(os_ver, 2, term_from_int32(ESP_IDF_VERSION_PATCH));
return os_ver;
}
return UNDEFINED_ATOM;
}
Expand Down
66 changes: 64 additions & 2 deletions src/platforms/generic_unix/lib/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,15 @@
#include "sys_mbedtls.h"
#endif

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>

Expand Down Expand Up @@ -550,8 +553,67 @@ Context *sys_create_port(GlobalContext *glb, const char *driver_name, term opts)

term sys_get_info(Context *ctx, term key)
{
UNUSED(ctx);
UNUSED(key);
if (key == globalcontext_make_atom(ctx->global, ATOM_STR("\x7", "os_type"))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using interop_atom_term_select_int for future-proofing, or anyway no need to make an atom for comparing it, globalcontext_is_term_equal_to_atom_string can be used instead.

struct utsname buf;
errno = 0;
if (uname(&buf) != 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (UNLIKELY(...))

return ERROR_ATOM;
}

const char *sys_name;

if (strcmp(buf.sysname, "Linux") == 0) {
sys_name = ATOM_STR("\x5", "linux");
} else if (strcmp(buf.sysname, "Darwin") == 0) {
sys_name = ATOM_STR("\x6", "darwin");
} else if (strcmp(buf.sysname, "FreeBSD") == 0) {
sys_name = ATOM_STR("\x7", "freebsd");
} else if (strcmp(buf.sysname, "NetBSB") == 0) {
sys_name = ATOM_STR("\x7", "netbsd");
} else if (strcmp(buf.sysname, "OpenBSB") == 0) {
sys_name = ATOM_STR("\x7", "openbsd");
} else if (strcmp(buf.sysname, "Firefly") == 0) {
sys_name = ATOM_STR("\x7", "firefly");
} else {
sys_name = ATOM_STR("\x7", "unknown");
}

if (memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os = term_alloc_tuple(2, &ctx->heap);
term_put_tuple_element(os, 0, globalcontext_make_atom(ctx->global, ATOM_STR("\x4", "unix")));
term_put_tuple_element(os, 1, globalcontext_make_atom(ctx->global, sys_name));
return os;
}
if (key == globalcontext_make_atom(ctx->global, ATOM_STR("\xA", "os_version"))) {
struct utsname buf;
char *p;
int ver[3] = { 0 };
int i = 0;

errno = 0;
if (uname(&buf) != 0) {
return ERROR_ATOM;
}
p = buf.release;
while ((*p) && i < 3) {
if (isdigit(*p)) {
ver[i] = strtol(p, &p, 10);
i++;
} else {
p++;
}
}
if (memory_ensure_free(ctx, TUPLE_SIZE(3)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os_ver = term_alloc_tuple(3, &ctx->heap);
term_put_tuple_element(os_ver, 0, term_from_int(ver[0]));
term_put_tuple_element(os_ver, 1, term_from_int(ver[1]));
term_put_tuple_element(os_ver, 2, term_from_int(ver[2]));
return os_ver;
}
return UNDEFINED_ATOM;
}

Expand Down
6 changes: 6 additions & 0 deletions src/platforms/rp2040/src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

cmake_minimum_required (VERSION 3.13)

include(${PICO_SDK_PATH}/pico_sdk_version.cmake)

set(HEADER_FILES
gpiodriver.h
platform_defaultatoms.h
Expand All @@ -44,6 +46,10 @@ set(
${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}
)

add_compile_definitions(OS_VERSION_MAJOR=${PICO_SDK_VERSION_MAJOR})
add_compile_definitions(OS_VERSION_MINOR=${PICO_SDK_VERSION_MINOR})
add_compile_definitions(OS_VERSION_PATCH=${PICO_SDK_VERSION_REVISION})

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's double-check. @pguyot are you aware of a cleaner approach?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find anything better, it looks like the usual approach.

Also, we may want an additional avm_ or atomvm_* key for the chip versions. Pico SDK has functions to query chip and rom versions. This could be in another PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we may want an additional avm_ or atomvm_* key for the chip versions. Pico SDK has functions to query chip and rom versions. This could be in another PR.

This would be good. Perhaps at the same time the ESP32 key esp_chip_info should be renamed, so that both (and eventually other) platforms can use the same key?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But maybe that kind of breakage needs to be based on main (0.7.0)? Or add a deprecation warning and support both versions of the key on esp32 for a cycle...

add_library(libAtomVM${PLATFORM_LIB_SUFFIX} STATIC ${SOURCE_FILES} ${HEADER_FILES})
target_compile_features(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC c_std_11)
if(CMAKE_COMPILER_IS_GNUCC)
Expand Down
22 changes: 20 additions & 2 deletions src/platforms/rp2040/src/lib/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,26 @@ Context *sys_create_port(GlobalContext *glb, const char *port_name, term opts)

term sys_get_info(Context *ctx, term key)
{
UNUSED(ctx);
UNUSED(key);
if (key == globalcontext_make_atom(ctx->global, ATOM_STR("\x7", "os_type"))) {
if (memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os = term_alloc_tuple(2, &ctx->heap);
term_put_tuple_element(os, 0, globalcontext_make_atom(ctx->global, ATOM_STR("\x6", "rp2040")));
term_put_tuple_element(os, 1, globalcontext_make_atom(ctx->global, ATOM_STR("\x8", "pico-sdk")));

return os;
}
if (key == globalcontext_make_atom(ctx->global, ATOM_STR("\xA", "os_version"))) {
if (memory_ensure_free(ctx, TUPLE_SIZE(3)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os_ver = term_alloc_tuple(3, &ctx->heap);
term_put_tuple_element(os_ver, 0, term_from_int32(OS_VERSION_MAJOR));
term_put_tuple_element(os_ver, 1, term_from_int32(OS_VERSION_MINOR));
term_put_tuple_element(os_ver, 2, term_from_int32(OS_VERSION_PATCH));
return os_ver;
}
return UNDEFINED_ATOM;
}

Expand Down
3 changes: 3 additions & 0 deletions src/platforms/stm32/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,7 @@ include(cmake/libopencm3.cmake)
# Include additional compilation flags
include(cmake/compile-flags.cmake)

# Include version_from_git
include(cmake/version_from_git.cmake)

add_subdirectory(src)
58 changes: 58 additions & 0 deletions src/platforms/stm32/cmake/version_from_git.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#
# This file is part of AtomVM.
#
# Copyright 2024 Winford (Uncle Grumpy) <[email protected]>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

function( version_from_git )

# Find Git
find_package(Git)
if(!Git_FOUND)
message( FATAL_ERROR "Git not found" )
Copy link
Collaborator

@bettio bettio Apr 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make unsupported source only releases (without a .git directory). Am I right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not consider that specific possibility, but there is no way exposed from libopencm3 to determine the version used for the build. If a source code release of libopencm3 was used, then the version returned would be {undefined,undefined,undefined}.

endif(!Git_FOUND)

# Git tag
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --exact-match
WORKING_DIRECTORY "${LIBOPENCM3_DIR}"
RESULT_VARIABLE git_result
OUTPUT_VARIABLE git_tag
ERROR_VARIABLE git_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
)
if( NOT git_result EQUAL 0 )
message( FATAL_ERROR "Failed to execute Git: ${git_error}" )
endif()

if( git_tag MATCHES "^v([0-9]+)[.]([0-9]+)[.]([0-9]+).*$" )
set( ide_version_major "${CMAKE_MATCH_1}" )
set( ide_version_minor "${CMAKE_MATCH_2}" )
set( ide_version_patch "${CMAKE_MATCH_3}" )
else()
set( ide_version_major "undefined" )
set( ide_version_minor "undefined" )
set( ide_version_patch "undefined" )
endif()

# Set parent scope variables
set( IDE_VERSION_MAJOR ${ide_version_major} PARENT_SCOPE)
set( IDE_VERSION_MINOR ${ide_version_minor} PARENT_SCOPE)
set( IDE_VERSION_PATCH ${ide_version_patch} PARENT_SCOPE)

endfunction( version_from_git )
6 changes: 6 additions & 0 deletions src/platforms/stm32/src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
cmake_minimum_required (VERSION 3.13)
project (libAtomVMPlatformSTM32)


set(HEADER_FILES
avm_devcfg.h
avm_log.h
Expand All @@ -41,6 +42,11 @@ set(
${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}
)

version_from_git()
add_compile_definitions(OS_VERSION_MAJOR=${IDE_VERSION_MAJOR})
add_compile_definitions(OS_VERSION_MINOR=${IDE_VERSION_MINOR})
add_compile_definitions(OS_VERSION_PATCH=${IDE_VERSION_PATCH})

add_library(libAtomVM${PLATFORM_LIB_SUFFIX} ${SOURCE_FILES} ${HEADER_FILES})
target_compile_features(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC c_std_11)
if(CMAKE_COMPILER_IS_GNUCC)
Expand Down
22 changes: 20 additions & 2 deletions src/platforms/stm32/src/lib/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,26 @@ Context *sys_create_port(GlobalContext *glb, const char *driver_name, term opts)

term sys_get_info(Context *ctx, term key)
{
UNUSED(ctx);
UNUSED(key);
if (key == globalcontext_make_atom(ctx->global, ATOM_STR("\x7", "os_type"))) {
if (memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os = term_alloc_tuple(2, &ctx->heap);
term_put_tuple_element(os, 0, globalcontext_make_atom(ctx->global, ATOM_STR("\x3", "stm32")));
term_put_tuple_element(os, 1, globalcontext_make_atom(ctx->global, ATOM_STR("\x7", "opencm3")));

return os;
}
if (key == globalcontext_make_atom(ctx->global, ATOM_STR("\xA", "os_version"))) {
if (memory_ensure_free(ctx, TUPLE_SIZE(3)) != MEMORY_GC_OK) {
return OUT_OF_MEMORY_ATOM;
}
term os_ver = term_alloc_tuple(3, &ctx->heap);
term_put_tuple_element(os_ver, 0, term_from_int32(OS_VERSION_MAJOR));
term_put_tuple_element(os_ver, 1, term_from_int32(OS_VERSION_MINOR));
term_put_tuple_element(os_ver, 2, term_from_int32(OS_VERSION_PATCH));
return os_ver;
}
return UNDEFINED_ATOM;
}

Expand Down