diff --git a/bundle.json b/bundle.json index e21c1c7d7893d990dac748024c4bf80467b1f046..44c9441c6910cd4639418e28cdddcd6bc4da2971 100644 --- a/bundle.json +++ b/bundle.json @@ -65,6 +65,16 @@ "header_base": "//base/hiviewdfx/hilog/interfaces/native/innerkits/include" } }, + { + "name": "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog_base_for_musl", + "header": { + "header_files": [ + "hilog_base/log_base.h" + ], + "header_base": "//base/hiviewdfx/hilog/interfaces/native/innerkits/include" + }, + "visibility": [ "musl" ] + }, { "name": "//base/hiviewdfx/hilog/interfaces/rust:hilog_rust", "header": { diff --git a/frameworks/libhilog/base/hilog_base.c b/frameworks/libhilog/base/hilog_base.c index c482e9c9bf495559d40d5d3051ea9fcc297fb83e..eab0f02032bdbb156ebda1e561131ef483fda733 100644 --- a/frameworks/libhilog/base/hilog_base.c +++ b/frameworks/libhilog/base/hilog_base.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -31,25 +32,61 @@ static const int SOCKET_TYPE = SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC; static const struct sockaddr_un SOCKET_ADDR = {AF_UNIX, SOCKET_FILE_DIR INPUT_SOCKET_NAME}; - -static int SendMessage(HilogMsg *header, const char *tag, uint16_t tagLen, const char *fmt, uint16_t fmtLen) +static const int INVALID_FD = -1; +static const int CAS_FAIL = -1; +static const int CAS_INVALID_PARAM = -2; +static const int CAS_SUCCESS = 0; +static const int INVALID_RESULT = -1; +static atomic_int g_socketFd = INVALID_FD; + +static void CleanSocket(void) __attribute__((destructor)); + +static int GenerateSocketFd(void) { - // The hilogbase interface cannot has mutex, so need to re-open and connect to the socketof the hilogd - // server each time you write logs. Althougth there is some overhead, you can only do this. int socketFd = TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCKET_TYPE, 0)); - if (socketFd < 0) { + if (socketFd == INVALID_FD) { dprintf(ERROR_FD, "HiLogBase: Can't create socket! Errno: %d\n", errno); - return socketFd; + return INVALID_FD; } - long int result = TEMP_FAILURE_RETRY(connect(socketFd, (const struct sockaddr *)(&SOCKET_ADDR), sizeof(SOCKET_ADDR))); - if (result < 0) { + if (result == INVALID_RESULT) { dprintf(ERROR_FD, "HiLogBase: Can't connect to server. Errno: %d\n", errno); - if (socketFd >= 0) { - close(socketFd); + close(socketFd); + return INVALID_FD; + } + return socketFd; +} + +static int CASGlobalSocketFd(int socketFd) +{ + if (socketFd == INVALID_FD) { + return CAS_INVALID_PARAM; + } + int expected = INVALID_FD; + // we should use CAS to avoid multi-thread problem + if (!atomic_compare_exchange_strong(&g_socketFd, &expected, socketFd)) { + // failure CAS: other threads execute to this branch to close extra fd + return CAS_FAIL; + } + // success CAS: only one thread can execute to this branch + return CAS_SUCCESS; +} + +static int SendMessage(HilogMsg *header, const char *tag, uint16_t tagLen, const char *fmt, uint16_t fmtLen) +{ + bool releaseSocket = false; + int socketFd = INVALID_FD; + // read fd by using atomic operation + socketFd = atomic_load(&g_socketFd); + if (socketFd == INVALID_FD) { + socketFd = GenerateSocketFd(); + int result = CASGlobalSocketFd(socketFd); + if (result == CAS_INVALID_PARAM) { + return INVALID_RESULT; + } else if (result == CAS_FAIL) { + releaseSocket = true; } - return result; } struct timespec ts = {0}; @@ -70,12 +107,21 @@ static int SendMessage(HilogMsg *header, const char *tag, uint16_t tagLen, const vec[2].iov_base = (void *)((char *)(fmt)); // 2 : index of log content vec[2].iov_len = fmtLen; // 2 : index of log content int ret = TEMP_FAILURE_RETRY(writev(socketFd, vec, LOG_LEN)); - if (socketFd >= 0) { + if (releaseSocket) { close(socketFd); } return ret; } +static void CleanSocket(void) +{ + int socketFd = atomic_load(&g_socketFd); + if (socketFd >= 0) { + close(socketFd); + atomic_store(&g_socketFd, INVALID_FD); + } +} + int HiLogBasePrintArgs( const LogType type, const LogLevel level, const unsigned int domain, const char *tag, const char *fmt, va_list ap) { @@ -117,4 +163,4 @@ bool HiLogBaseIsLoggable(unsigned int domain, const char *tag, LogLevel level) return false; } return true; -} +} \ No newline at end of file diff --git a/interfaces/native/innerkits/BUILD.gn b/interfaces/native/innerkits/BUILD.gn index bdb885ec6da9e0f26e1a8709bee54d0df6e147f6..26c638ba7d055132617bde8cb9ff983aed62cf08 100644 --- a/interfaces/native/innerkits/BUILD.gn +++ b/interfaces/native/innerkits/BUILD.gn @@ -128,6 +128,36 @@ config("libhilog_base_config") { include_dirs = [ "../../../frameworks/libhilog/include" ] } +# part of default_compiler_configs from build/config/BUILDCONFIG.gn +musl_inherited_configs_for_musl = [ + "//build/config/compiler:afdo", + "//build/config/compiler:afdo_optimize_size", + "//build/config/compiler:compiler", + "//build/config/compiler:compiler_arm_fpu", + "//build/config/compiler:compiler_arm_thumb", + "//build/config/compiler:chromium_code", + "//build/config/compiler:default_include_dirs", + "//build/config/compiler:default_optimization", + "//build/config/compiler:default_stack_frames", + "//build/config/compiler:default_symbols", + "//build/config/compiler:export_dynamic", + "//build/config/compiler:no_exceptions", + "//build/config/compiler:no_rtti", + "//build/config/compiler:runtime_library", + "//build/config/sanitizers:default_sanitizer_flags", +] + +ohos_static_library("libhilog_base_for_musl") { + print("************libhilog_base use_musl ***************") + ldflags = [ "-nostdlib" ] + remove_configs = musl_inherited_configs_for_musl + configs = [ "//build/config/components/musl:soft_musl_config" ] + public_configs = [ ":libhilog_base_pub_cfg" ] + + subsystem_name = "hiviewdfx" + part_name = "hilog" +} + ohos_static_library("libhilog_base") { public_configs = [ ":libhilog_base_pub_cfg" ] diff --git a/test/BUILD.gn b/test/BUILD.gn index 7a55ee1b605747f1528defc82b356d4c35b6a09a..87822317a8bdf0e0a880b5b2030b98c6293d8781 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -30,6 +30,7 @@ ohos_moduletest("HiLogNDKTest") { configs = [ ":module_private_config" ] external_deps = [ + "bounds_checking_function:libsec_shared", "hilog:libhilog", "hilog:libhilog_base", "init:libbegetutil", diff --git a/test/moduletest/common/hilog_base_ndk_test.cpp b/test/moduletest/common/hilog_base_ndk_test.cpp index e8b2af21d2de2715f82a6f4abf47f6ddde8fb3d2..9f91a7e315579cdd2c676774bd4b50970793bd4c 100644 --- a/test/moduletest/common/hilog_base_ndk_test.cpp +++ b/test/moduletest/common/hilog_base_ndk_test.cpp @@ -15,10 +15,18 @@ #include #include +#include #include +#include #include +#include #include +#include #include +#include +#include +#include +#include #include #include @@ -41,6 +49,10 @@ static constexpr unsigned int MORE_LOGS = 100; static constexpr unsigned int SHORT_LOG = 16; static constexpr unsigned int LONG_LOG = 1000; static constexpr unsigned int VERY_LONG_LOG = 2048; +static constexpr unsigned int THREAD_COUNT = 10; +static std::string g_str[THREAD_COUNT]; +#define TWO (2) +#define NEGATIVE_ONE (-1) enum LogInterfaceType { DEBUG_METHOD = 0, @@ -92,6 +104,100 @@ static std::string PopenToString(const std::string &command) return str; } +// Function executed by the subthread to print logs +void FunctionPrintLog(int index) +{ + HiLogBasePrint(LOG_INIT, LOG_ERROR, 0xD002D00, LOG_TAG, "FunctionPrintLog %{public}s", g_str[index].c_str()); +} + +//Check whether corresponding logs are generated. If yes, true is returned. If no, false is returned. +bool CheckHiLogPrint(char *needToMatch) +{ + constexpr int COMMAND_SIZE = 50; + constexpr int BUFFER_SIZE = 1024; + constexpr int FAIL_CLOSE = -1; + bool ret = false; + // Use the system function to read the current hilog information. + char command[] = "/bin/hilog -x | grep "; + char finalCommand[COMMAND_SIZE]; + int res = snprintf_s(finalCommand, COMMAND_SIZE, COMMAND_SIZE - 1, "%s%s", command, needToMatch); + if (res == NEGATIVE_ONE) { + printf("CheckHiLogPrint command generate snprintf_s failed\n"); + return false; + } + finalCommand[COMMAND_SIZE - 1] = '\0'; + char buffer[BUFFER_SIZE]; + FILE* pipe = popen(finalCommand, "r"); + if (pipe == nullptr) { + printf("CheckHiLogPrint: Failed to run command\n"); + return false; + } + + // Read command output and print + while (fgets(buffer, BUFFER_SIZE, pipe) != nullptr) { + printf("%s", buffer); + ret = true; + } + + // Close the pipe and get the return value + int returnValue = pclose(pipe); + if (returnValue == FAIL_CLOSE) { + printf("CheckHiLogPrint pclose failed returnValue=-1 errno=%d\n", errno); + } + return ret; +} + +// Detects how many FDs are linked to hilogd. +int CheckHiLogdLinked() +{ + #define PROC_PATH_LENGTH (64) + int result = 0; + char procPath[PROC_PATH_LENGTH]; + int res = snprintf_s(procPath, PROC_PATH_LENGTH, PROC_PATH_LENGTH - 1, "/proc/%d/fd", getpid()); + if (res == NEGATIVE_ONE) { + printf("CheckHiLogdLinked getpid snprintf_s failed\n"); + return 0; + } + DIR *dir = opendir(procPath); + if (dir == nullptr) { + return result; + } + struct dirent *entry; + while ((entry = readdir(dir)) != nullptr) { + if (entry->d_type != DT_LNK) { + continue; + } + char fdPath[128]; + res = snprintf_s(fdPath, sizeof(fdPath), sizeof(fdPath) - 1, "%s/%s", procPath, entry->d_name); + if (res == NEGATIVE_ONE) { + printf("CheckHiLogdLinked fd search snprintf_s failed\n"); + return 0; + } + + char target[256]; + ssize_t len = readlink(fdPath, target, sizeof(target) - 1); + if (len == -1) { + continue; + } + target[len] = '\0'; + if (!strstr(target, "socket")) { + continue; + } + struct sockaddr_un addr; + socklen_t addrLen = sizeof(addr); + + // Obtains the peer address connected to the socket. + getpeername(atoi(entry->d_name), reinterpret_cast(&addr), &addrLen); + if (strstr(addr.sun_path, "hilogInput")) { + printf("FD: %s Connected to: %s\n", entry->d_name, addr.sun_path); + result++; + } + } + + closedir(dir); + return result; +} + class HiLogBaseNDKTest : public testing::Test { public: static void SetUpTestCase(); @@ -231,6 +337,27 @@ HWTEST_F(HiLogBaseNDKTest, IsLoggable, TestSize.Level1) EXPECT_FALSE(HiLogBaseIsLoggable(0xD002D00, "abc", LOG_LEVEL_MIN)); } +HWTEST_F(HiLogBaseNDKTest, HilogBasePrintCheck, TestSize.Level1) +{ + std::thread threads[THREAD_COUNT]; + // Create threads and print logs concurrently. Logs printed by each thread cannot be lost. + for (int i = 0; i < THREAD_COUNT; i++) { + // Generate a random string, and then pass the subscript to the child thread through the value, + // so that the child thread can obtain the global data. + g_str[i] = RandomStringGenerator(); + threads[i] = std::thread(FunctionPrintLog, i); + } + // Wait for the thread execution to complete. + for (int i = 0; i < THREAD_COUNT; i++) { + threads[i].join(); + bool result = CheckHiLogPrint(g_str[i].data()); + EXPECT_EQ(result, true); + } + + // Check the number of socket links to hilogInput. + int result = CheckHiLogdLinked(); + EXPECT_EQ(result, TWO); +} } // namespace HiLogTest } // namespace HiviewDFX -} // namespace OHOS +} // namespace OHOS \ No newline at end of file