From 57b5154f07b8b7509cceb53c1b19436736ef0428 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 27 Aug 2020 10:10:28 +0200 Subject: [PATCH] Add Diagnostics API Original authors: - library & tct: Konrad Kuchciak - unit tests: Wiktor Gerstenstein - fixes: Mateusz Moscicki Change-Id: Ic7f7443fc1c88dd69e25fb94cd87e606e96e5dc0 Signed-off-by: Konrad Kuchciak --- CMakeLists.txt | 30 + LICENSE.APLv2 | 204 ++++++ NOTICE | 3 + doc/diagnostics_doc.h | 41 ++ include/diagnostics.h | 199 ++++++ packaging/diagnostics.manifest | 5 + packaging/diagnostics.spec | 60 ++ src/library/CMakeLists.txt | 36 + src/library/dbus.c | 193 +++++ src/library/dbus.h | 62 ++ src/library/diagnostics.c | 359 ++++++++++ src/library/diagnostics.pc.in | 13 + src/library/log.h | 58 ++ src/library/signal.c | 35 + src/library/signal.h | 44 ++ src/test/CMakeLists.txt | 99 +++ src/test/tct-support/.gitignore | 1 + src/test/tct-support/README.md | 19 + src/test/tct-support/crash-test/.cproject | 660 +++++++++++++++++ src/test/tct-support/crash-test/.exportMap | 5 + src/test/tct-support/crash-test/.gitignore | 3 + .../tct-support/crash-test/.package-stamp | 1 + src/test/tct-support/crash-test/.project | 46 ++ src/test/tct-support/crash-test/.tproject | 12 + .../tct-support/crash-test/inc/crash-test.h | 28 + .../tct-support/crash-test/project_def.prop | 11 + .../crash-test/shared/res/crash-test.png | Bin 0 -> 57662 bytes .../tct-support/crash-test/src/crash-test.c | 81 +++ .../tct-support/crash-test/tizen-manifest.xml | 8 + src/test/tct-support/dumpsys-client/.cproject | 663 ++++++++++++++++++ .../tct-support/dumpsys-client/.exportMap | 5 + .../tct-support/dumpsys-client/.gitignore | 3 + .../tct-support/dumpsys-client/.package-stamp | 1 + src/test/tct-support/dumpsys-client/.project | 46 ++ src/test/tct-support/dumpsys-client/.tproject | 12 + .../dumpsys-client/inc/dumpsys-client.h | 28 + .../dumpsys-client/project_def.prop | 11 + .../shared/res/dumpsys-client.png | Bin 0 -> 57662 bytes .../dumpsys-client/src/dumpsys-client.c | 140 ++++ .../dumpsys-client/tizen-manifest.xml | 8 + src/test/test_diagnostics.h | 98 +++ src/test/test_diagnostics_add_function_defs.h | 47 ++ src/test/test_diagnostics_create.c | 84 +++ src/test/test_diagnostics_data_create.c | 55 ++ src/test/test_diagnostics_data_read.c | 139 ++++ src/test/test_diagnostics_destroy.c | 71 ++ src/test/test_diagnostics_get_client_id.c | 66 ++ src/test/test_diagnostics_get_data.c | 96 +++ src/test/test_diagnostics_get_report_path.c | 84 +++ .../test_diagnostics_request_client_data.c | 125 ++++ .../test_diagnostics_set_notification_cb.c | 85 +++ src/test/test_diagnostics_signal_handler.c | 73 ++ 52 files changed, 4256 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE.APLv2 create mode 100644 NOTICE create mode 100644 doc/diagnostics_doc.h create mode 100644 include/diagnostics.h create mode 100644 packaging/diagnostics.manifest create mode 100644 packaging/diagnostics.spec create mode 100644 src/library/CMakeLists.txt create mode 100644 src/library/dbus.c create mode 100644 src/library/dbus.h create mode 100644 src/library/diagnostics.c create mode 100644 src/library/diagnostics.pc.in create mode 100644 src/library/log.h create mode 100644 src/library/signal.c create mode 100644 src/library/signal.h create mode 100644 src/test/CMakeLists.txt create mode 100644 src/test/tct-support/.gitignore create mode 100644 src/test/tct-support/README.md create mode 100644 src/test/tct-support/crash-test/.cproject create mode 100644 src/test/tct-support/crash-test/.exportMap create mode 100644 src/test/tct-support/crash-test/.gitignore create mode 100644 src/test/tct-support/crash-test/.package-stamp create mode 100644 src/test/tct-support/crash-test/.project create mode 100644 src/test/tct-support/crash-test/.tproject create mode 100644 src/test/tct-support/crash-test/inc/crash-test.h create mode 100644 src/test/tct-support/crash-test/project_def.prop create mode 100644 src/test/tct-support/crash-test/shared/res/crash-test.png create mode 100644 src/test/tct-support/crash-test/src/crash-test.c create mode 100644 src/test/tct-support/crash-test/tizen-manifest.xml create mode 100644 src/test/tct-support/dumpsys-client/.cproject create mode 100644 src/test/tct-support/dumpsys-client/.exportMap create mode 100644 src/test/tct-support/dumpsys-client/.gitignore create mode 100644 src/test/tct-support/dumpsys-client/.package-stamp create mode 100644 src/test/tct-support/dumpsys-client/.project create mode 100644 src/test/tct-support/dumpsys-client/.tproject create mode 100644 src/test/tct-support/dumpsys-client/inc/dumpsys-client.h create mode 100644 src/test/tct-support/dumpsys-client/project_def.prop create mode 100644 src/test/tct-support/dumpsys-client/shared/res/dumpsys-client.png create mode 100644 src/test/tct-support/dumpsys-client/src/dumpsys-client.c create mode 100644 src/test/tct-support/dumpsys-client/tizen-manifest.xml create mode 100644 src/test/test_diagnostics.h create mode 100644 src/test/test_diagnostics_add_function_defs.h create mode 100644 src/test/test_diagnostics_create.c create mode 100644 src/test/test_diagnostics_data_create.c create mode 100644 src/test/test_diagnostics_data_read.c create mode 100644 src/test/test_diagnostics_destroy.c create mode 100644 src/test/test_diagnostics_get_client_id.c create mode 100644 src/test/test_diagnostics_get_data.c create mode 100644 src/test/test_diagnostics_get_report_path.c create mode 100644 src/test/test_diagnostics_request_client_data.c create mode 100644 src/test/test_diagnostics_set_notification_cb.c create mode 100644 src/test/test_diagnostics_signal_handler.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2c7a2a2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(diagnostics LANGUAGES C) +SET(target ${PROJECT_NAME}) +SET(dependency "glib-2.0 gio-unix-2.0 dlog dumpsys capi-system-info") +# ADD_DEFINITIONS(-Wall -Werror -Wextra) + +# Options +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED "${dependency}") + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -flto -Wall -Werror -Winline") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +ADD_SUBDIRECTORY(src/library) + +# cmocka tasts and coverage +option(ENABLE_COVERAGE "Measure the coverage and generate report" OFF) +option(ENABLE_TESTS "Run unit tests after build" ON) +if(ENABLE_TESTS OR ENABLE_COVERAGE) + FIND_PACKAGE(CMocka CONFIG REQUIRED) + enable_testing() + include(CTest) + INCLUDE_DIRECTORIES(${LIBCMOCKA_INCLUDE_DIR}) + ADD_SUBDIRECTORY(src/test) +endif(ENABLE_TESTS OR ENABLE_COVERAGE) diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 index 0000000..9bbfde4 --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,204 @@ +Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..c6ee7fa --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE.APLv2 file for Apache license, version 2 terms and conditions. diff --git a/doc/diagnostics_doc.h b/doc/diagnostics_doc.h new file mode 100644 index 0000000..40ab841 --- /dev/null +++ b/doc/diagnostics_doc.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +/** + * @ingroup CAPI_SYSTEM_FRAMEWORK + * @defgroup CAPI_SYSTEM_DIAGNOSTICS_MODULE Diagnostics + * @brief The @ref CAPI_SYSTEM_DIAGNOSTICS_MODULE API provides functions to receive crash reports and request logs from other apps. + * + * @section CAPI_SYSTEM_DIAGNOSTICS_MODULE_HEADER Required Header + * \#include + * + * @section CAPI_SYSTEM_DIAGNOSTICS_MODULE_OVERVIEW Overview + * This @ref CAPI_SYSTEM_DIAGNOSTICS_MODULE API allows applications to receive notification about newly created crash report, + * as well as request other apps to dump their logs in real time. Moreover, functions for reading crash and log contents are provided. + * + * @section CAPI_SYSTEM_DIAGNOSTICS_MODULE_FEATURE Realted Features + * + * This API is related with the following feature:\n + * - http://tizen.org/feature/diagnostics\n + * + * It is recommended to design feature related codes in your application for reliability.\n + * + * You can check if a device supports the related features for this API by using @ref CAPI_SYSTEM_SYSTEM_INFO_MODULE,thereby controlling the procedure of your application.\n + * + * To ensure your application is only running on the device with specific features, please define the features in your manifest file using the manifest editor in the SDK.\n + * + * More details on featuring your application can be found from Feature Element. + */ diff --git a/include/diagnostics.h b/include/diagnostics.h new file mode 100644 index 0000000..4f07bb4 --- /dev/null +++ b/include/diagnostics.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __TIZEN_SYSTEM_DIAGNOSTICS_H__ +#define __TIZEN_SYSTEM_DIAGNOSTICS_H__ + +#ifdef __cplusplus +extern "C" { +# endif + +#include +#include + +/** + * @brief Diagnostics context. + * @since_tizen 6.0 + */ +typedef void* diagnostics_ctx_h; + +/** + * @brief Diagnostics data. + * @since_tizen 6.0 + */ +typedef void* diagnostics_data_h; + +/** + * @brief Enumeration for error codes of Diagnostics. + * @since_tizen 6.0 + */ +typedef enum { + DIAGNOSTICS_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + DIAGNOSTICS_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + DIAGNOSTICS_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR, /**< I/O error */ + DIAGNOSTICS_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */ + DIAGNOSTICS_ERROR_RESOURCE_BUSY = TIZEN_ERROR_RESOURCE_BUSY, /**< Device or resource busy */ + DIAGNOSTICS_ERROR_TIMED_OUT = TIZEN_ERROR_TIMED_OUT, /**< Time out */ + DIAGNOSTICS_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< Not supported */ + DIAGNOSTICS_ERROR_TRY_AGAIN = TIZEN_ERROR_TRY_AGAIN, /**< Try again */ + DIAGNOSTICS_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED /**< Permission denied */ +} diagnostics_error_e; + +/** + * @brief Notification callback fired when new bug report arrives. + * @since_tizen 6.0 + * @remarks @a ctx should be released with diagnostics_destroy() + * + * @param[in] ctx Diagnostics context handle + * @param[in] user_data The user data passed from the callback registration function + */ +typedef void(*diagnostics_notification_cb)(diagnostics_ctx_h ctx, void *user_data); + +/** + * @brief Sets the callback for bug report notification. + * @since_tizen 6.0 + * + * @param[in] callback A callback function to set + * @param[in] user_data The user data to be passed to the callback function + * + * @return 0 on success, otherwise a negative error value + * @retval #DIAGNOSTICS_ERROR_NONE Success + * @retval #DIAGNOSTICS_ERROR_NOT_SUPPORTED Not supported + * @retval #DIAGNOSTICS_ERROR_INVALID_PARAMETER Provided parameter is invalid + * @retval #DIAGNOSTICS_ERROR_RESOURCE_BUSY Callback already registered + * @retval #DIAGNOSTICS_ERROR_IO_ERROR Internal error occured + */ +int diagnostics_set_notification_cb(diagnostics_notification_cb callback, void *user_data); + +/** + * @brief Unsets the callback for bug report notification. + * @since_tizen 6.0 + * + * @return 0 on success, otherwise a negative error value + * @retval #DIAGNOSTICS_ERROR_NONE Success + * @retval #DIAGNOSTICS_ERROR_NOT_SUPPORTED Not supported + * @retval #DIAGNOSTICS_ERROR_IO_ERROR Internal error occured + */ +int diagnostics_unset_notification_cb(void); + +/** + * @platform + * @brief Requests client to dump data. + * @since_tizen 6.0 + * @privlevel platform + * @privilege + * @remarks @a data should be released with diagnostics_data_destroy(). + * This function is permitted only to an app signed by platform level certificates. + * + * @param[in] client_id An id of app or service to request + * @param[in] params Array of parameters + * @param[in] params_size Number of parameters + * @param[out] data Dumpsys data handle + * + * @return 0 on success, otherwise a negative error value + * @retval #DIAGNOSTICS_ERROR_NONE Success + * @retval #DIAGNOSTICS_ERROR_NOT_SUPPORTED Not supported + * @retval #DIAGNOSTICS_ERROR_PERMISSION_DENIED Permission denied + * @retval #DIAGNOSTICS_ERROR_INVALID_PARAMETER Provided parameter is invalid + * @retval #DIAGNOSTICS_ERROR_IO_ERROR Internal error occured + * @retval #DIAGNOSTICS_ERROR_OUT_OF_MEMORY Not enough memory to create data handle + */ +int diagnostics_request_client_data(const char *client_id, const char **params, int params_size, diagnostics_data_h *data); + +/** + * @brief Reads diagnostics data. + * @since_tizen 6.0 + * @remarks @a data should be released with diagnostics_data_destroy(). + * This function is intended for use in loop until EOF is reached. + * EOF is when @a bytes_read == 0 and function returns #DIAGNOSTICS_ERROR_NONE. + * @param[in] data Diagnostics data handle + * @param[in,out] buf Buffer to store read data \n + * Provided buffer must be large enough to contain @a count number of bytes + * @param[in] count Number of bytes to read + * @param[in] timeout_ms Timeout [ms] for reading requested number of bytes (timeout_ms <= 0 means to wait forever) + * @param[out] bytes_read Real number of read bytes + * + * @return 0 on success, otherwise a negative error value + * @retval #DIAGNOSTICS_ERROR_NONE Success + * @retval #DIAGNOSTICS_ERROR_NOT_SUPPORTED Not supported + * @retval #DIAGNOSTICS_ERROR_INVALID_PARAMETER Provided parameter is invalid + * @retval #DIAGNOSTICS_ERROR_TIMED_OUT Timeout occured + * @retval #DIAGNOSTICS_ERROR_TRY_AGAIN Try again + * @retval #DIAGNOSTICS_ERROR_IO_ERROR Internal error occured while trying to read data, result is unspecified and *bytes_read is not updated + */ +int diagnostics_data_read(diagnostics_data_h data, void *buf, size_t count, int timeout_ms, size_t *bytes_read); + +/** + * @brief Frees diagnostics data. + * @since_tizen 6.0 + * + * @param[in] data Diagnostics data handle + */ +void diagnostics_data_destroy(diagnostics_data_h data); + +/** + * @brief Gets diagnostics context provider's id. + * @since_tizen 6.0 + * @remarks @a client_id should be released with free(). + * + * @param[in] ctx Diagnostics context handle + * @param[out] client_id An id of the context provider + * + * @return 0 on success, otherwise a negative error value + * @retval #DIAGNOSTICS_ERROR_NONE Success + * @retval #DIAGNOSTICS_ERROR_NOT_SUPPORTED Not supported + * @retval #DIAGNOSTICS_ERROR_INVALID_PARAMETER Provided parameter is invalid + */ +int diagnostics_get_client_id(diagnostics_ctx_h ctx, char **client_id); + +/** + * @platform + * @brief Gets report data. + * @since_tizen 6.0 + * @privlevel platform + * @privilege + * @remarks @a data should be released with diagnostics_data_destroy(). + * This function is permitted only to an app signed by platform level certificates. + * + * @param[in] ctx Diagnostics context handle + * @param[in] params Array of parameters \n + * Refer to context provider's documentation for available parameters + * @param[in] params_size Number of parameters + * @param[out] data Diagnostics data handle + * + * @return 0 on success, otherwise a negative error value + * @retval #DIAGNOSTICS_ERROR_NONE Success + * @retval #DIAGNOSTICS_ERROR_NOT_SUPPORTED Diagnostics data not supported with given context and parameters + * @retval #DIAGNOSTICS_ERROR_PERMISSION_DENIED Permission denied + * @retval #DIAGNOSTICS_ERROR_INVALID_PARAMETER Provided parameter is invalid + * @retval #DIAGNOSTICS_ERROR_IO_ERROR Internal error occured + * @retval #DIAGNOSTICS_ERROR_OUT_OF_MEMORY Not enough memory to create data handle + */ +int diagnostics_get_data(diagnostics_ctx_h ctx, const char **params, int params_size, diagnostics_data_h *data); + +/** + * @brief Frees diagnostics context. + * @since_tizen 6.0 + * + * @param[in] ctx Diagnostics context handle + */ +void diagnostics_destroy(diagnostics_ctx_h ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* __TIZEN_SYSTEM_DIAGNOSTICS_H__ */ diff --git a/packaging/diagnostics.manifest b/packaging/diagnostics.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/packaging/diagnostics.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/diagnostics.spec b/packaging/diagnostics.spec new file mode 100644 index 0000000..cc8a50e --- /dev/null +++ b/packaging/diagnostics.spec @@ -0,0 +1,60 @@ +Name: diagnostics +Version: 1.0.0 +Release: 1 +License: Apache-2.0 +Summary: Tizen Diagnostics API +Group: Diagnostics +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake +BuildRequires: cmocka +BuildRequires: cmocka-devel +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gio-unix-2.0) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(dumpsys) +BuildRequires: pkgconfig(capi-system-info) +BuildRequires: glib2-devel +%if 0%{?gcov:1} +BuildRequires: lcov +%else +%define gcov 0 +%endif + +Requires: dumpsys +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description +Tizen Diagnostics API + +%package devel +Summary: Tizen Diagnostics API (Development) +Group: Development/System +Requires: %{name} = %{version}-%{release} + +%description devel +Tizen Diagnostics API (Development) + +%prep +%setup -q + +%build +MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` +%cmake . -DMAJORVER=${MAJORVER} -DFULLVER=%{version} -DENABLE_COVERAGE=%{gcov} +make %{?jobs:-j%jobs} + +%install +%make_install + +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig + +%files +%manifest packaging/%{name}.manifest +%{_libdir}/*.so.* +%license LICENSE.APLv2 + +%files devel +%{_includedir}/*.h +%{_libdir}/*.so +%{_libdir}/pkgconfig/*.pc diff --git a/src/library/CMakeLists.txt b/src/library/CMakeLists.txt new file mode 100644 index 0000000..1fef613 --- /dev/null +++ b/src/library/CMakeLists.txt @@ -0,0 +1,36 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +AUX_SOURCE_DIRECTORY(. SOURCES) + +# Build +ADD_LIBRARY(${target} SHARED ${SOURCES}) +SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_FLAGS "${EXTRA_CFLAGS} -fPIE") +SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS "-pie") +SET_TARGET_PROPERTIES(${target} PROPERTIES SOVERSION ${MAJORVER}) +SET_TARGET_PROPERTIES(${target} PROPERTIES VERSION ${FULLVER}) +TARGET_LINK_LIBRARIES(${target} ${pkgs_LDFLAGS}) + +ADD_LIBRARY(${target}_static STATIC ${SOURCES}) +TARGET_INCLUDE_DIRECTORIES(${target}_static PRIVATE ${CMAKE_SOURCE_DIR}/src/library) +TARGET_COMPILE_OPTIONS(${target}_static BEFORE PRIVATE -include ../test/test_diagnostics_add_function_defs.h) +TARGET_COMPILE_DEFINITIONS(${target}_static PUBLIC -DUNIT_TEST) +if(ENABLE_COVERAGE) +TARGET_COMPILE_OPTIONS(${target}_static PUBLIC -g -fprofile-arcs -ftest-coverage) +endif(ENABLE_COVERAGE) + +# Make pkg-confing +SET(VERSION ${FULLVER}) +SET(PC_PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(PC_NAME ${PROJECT_NAME}) +SET(PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}") +SET(PC_INCLUDE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${inc_subdir}") +SET(PC_DESCRIPTION "Tizen Diagnostics API") +SET(PC_REQUIRED ${dependency}) +SET(PC_LDFLAGS -l${target}) + +CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/src/library/${PROJECT_NAME}.pc.in ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc @ONLY) + +# Install +INSTALL(TARGETS ${target} DESTINATION ${LIB_INSTALL_DIR}) +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) diff --git a/src/library/dbus.c b/src/library/dbus.c new file mode 100644 index 0000000..527b5fa --- /dev/null +++ b/src/library/dbus.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include + +#include "dbus.h" +#include "log.h" + +static int subscription_id = 0; + +static GDBusConnection *dbus_connect() +{ + GDBusConnection *conn; + GError *err = NULL; + + conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err); + if (!conn) { + _E("Unable to connect to system bus: %s", err->message); + g_error_free(err); + return NULL; + } + + return conn; +} + +MOCKABLE_FOR_TESTS +int dbus_subscribe(void (*signal_handler)(GDBusConnection *, + const gchar *, + const gchar *, + const gchar *, + const gchar *, + GVariant *, + gpointer)) +{ + GDBusConnection *conn = dbus_connect(); + int ret; + + if (!conn) + return -1; + + ret = g_dbus_connection_signal_subscribe(conn, + NULL, + NULL, + DBUS_MEMBER_CRASH, + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + signal_handler, + NULL, + NULL); + if (ret == FALSE) { + _E("Unable to subscribe to crash signal"); + return -EIO; + } + + subscription_id = ret; + + return 0; +} + +void dbus_unsubscribe(void) +{ + GDBusConnection *conn = dbus_connect(); + + if (!conn) + return; + + if (subscription_id) + g_dbus_connection_signal_unsubscribe(conn, subscription_id); + + subscription_id = 0; +} + +MOCKABLE_FOR_TESTS +int dbus_get_file_from_report(const char *report_path, const int entry, int *fd) +{ + GDBusConnection *conn = dbus_connect(); + GDBusMessage *message = NULL; + GDBusMessage *reply = NULL; + GVariant *parameters = NULL; + GUnixFDList *fd_list = NULL; + GError *error = NULL; + int fd_out = -1; + int ret = -1; + + if (!conn) + goto finish; + + message = g_dbus_message_new_method_call(DBUS_BUS_NAME, + DBUS_OBJECT_PATH, + DBUS_INTERFACE_NAME, + DBUS_METHOD_GET_FILE); + parameters = g_variant_new("(si)", report_path, entry); + g_dbus_message_set_body(message, parameters); + + reply = g_dbus_connection_send_message_with_reply_sync(conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + 10000, + NULL, + NULL, + &error); + + // _D("MESSAGE: \n%s", g_dbus_message_print(message, 4)); + // _D("REPLY: \n%s", g_dbus_message_print(reply, 4)); + + if (reply != NULL && g_dbus_message_get_message_type(reply) == G_DBUS_MESSAGE_TYPE_ERROR) + g_dbus_message_to_gerror(reply, &error); + + if (error != NULL) { + _E("Send message error: %s\n", error ? error->message : "unknown error"); + ret = error->code == G_DBUS_ERROR_ACCESS_DENIED ? -EACCES : -1; + goto finish; + } + + fd_list = g_dbus_message_get_unix_fd_list(reply); + if (!fd_list) { + _E("Method call get_file() haven't returned file descriptor"); + ret = -1; + goto finish; + } + + fd_out = g_unix_fd_list_get(fd_list, 0, &error); + if (fd_out == -1 || error) { + _E("g_unix_fd_list_get() failed: %s", error ? error->message : ""); + ret = -1; + goto finish; + } + + ret = 0; + +finish: + *fd = fd_out; + if (parameters) + g_variant_unref(parameters); + if (message) + g_object_unref(message); + if (reply) + g_object_unref(reply); + if (error) + g_error_free(error); + return ret; +} + +struct dbus_signal_s *dbus_signal_create(const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters) +{ + struct dbus_signal_s *signal; + + signal = calloc(1, sizeof(struct dbus_signal_s)); + if (!signal) { + _E("Unable to allocate memory"); + return NULL; + } + + signal->sender_name = g_strdup(sender_name); + signal->object_path = g_strdup(object_path); + signal->interface_name = g_strdup(interface_name); + signal->signal_name = g_strdup(signal_name); + signal->parameters = parameters; + g_variant_ref(signal->parameters); + + return signal; +} + +void dbus_signal_cleanup(struct dbus_signal_s *signal) +{ + if (!signal) + return; + + g_free(signal->sender_name); + g_free(signal->object_path); + g_free(signal->interface_name); + g_free(signal->signal_name); + g_variant_unref(signal->parameters); + free(signal); +} diff --git a/src/library/dbus.h b/src/library/dbus.h new file mode 100644 index 0000000..69c454a --- /dev/null +++ b/src/library/dbus.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __DBUS_H__ +#define __DBUS_H__ + +#define DBUS_MEMBER_CRASH "ProcessCrashedEx" +#define DBUS_SENDER_CRASH "org.tizen.system.crash" + +#define DBUS_BUS_NAME "org.tizen.system.diagnostics" +#define DBUS_OBJECT_PATH "/Org/Tizen/System/Diagnostics" +#define DBUS_INTERFACE_NAME DBUS_BUS_NAME +#define DBUS_METHOD_GET_FILE "get_file" + +#include +#include +#include + +#ifdef UNIT_TEST +#define MOCKABLE_FOR_TESTS __attribute__((weak)) +#else +#define MOCKABLE_FOR_TESTS +#endif + +struct dbus_signal_s { + gchar *sender_name; + gchar *object_path; + gchar *interface_name; + gchar *signal_name; + GVariant *parameters; +}; + +int dbus_subscribe(void (*signal_handler)(GDBusConnection *, + const gchar *, + const gchar *, + const gchar *, + const gchar *, + GVariant *, + gpointer)); +void dbus_unsubscribe(void); +int dbus_get_file_from_report(const char *report_path, const int entry, int *fd); +struct dbus_signal_s *dbus_signal_create(const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters); +void dbus_signal_cleanup(struct dbus_signal_s *signal); + +#endif /* __DBUS_H__ */ diff --git a/src/library/diagnostics.c b/src/library/diagnostics.c new file mode 100644 index 0000000..55501fe --- /dev/null +++ b/src/library/diagnostics.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "signal.h" +#include "dbus.h" + +#ifndef STATIC +#define STATIC static +#endif + +#define DIAGNOSTICS_FEATURE "http://tizen.org/feature/diagnostics" + +#define FEATURE_UNKNOWN -1 +#define FEATURE_FALSE 0 +#define FEATURE_TRUE 1 + +STATIC struct _diagnostics_cb_info_s { + diagnostics_notification_cb cb; + void *user_data; +} cb_info = { + NULL, + NULL +}; + +struct _diagnostics_ctx_s { + char *client_id; + struct dbus_signal_s *signal; + signal_type_e signal_type; +}; + +struct _diagnostics_data_s { + int fd; +}; + +STATIC int diagnostics_feature = FEATURE_UNKNOWN; + +STATIC bool __is_feature_supported(void) +{ + int ret = SYSTEM_INFO_ERROR_NONE; + bool feature = false; + + if (diagnostics_feature == FEATURE_UNKNOWN) { + ret = system_info_get_platform_bool(DIAGNOSTICS_FEATURE, &feature); + RETVM_IF(ret != SYSTEM_INFO_ERROR_NONE, false, "Failed to get system info"); + + diagnostics_feature = (feature ? FEATURE_TRUE : FEATURE_FALSE); + } + + return (diagnostics_feature == FEATURE_TRUE ? true : false); +} + +STATIC struct _diagnostics_ctx_s *diagnostics_create(struct dbus_signal_s *signal) +{ + RETV_IF(signal == NULL, NULL); + + struct _diagnostics_ctx_s *ctx; + + ctx = calloc(1, sizeof(struct _diagnostics_ctx_s)); + if (!ctx) { + _E("Unable to allocate memory"); + return NULL; + } + + if (signal->signal_name && strcmp(signal->signal_name, DBUS_MEMBER_CRASH) == 0) { + ctx->client_id = DBUS_SENDER_CRASH; + ctx->signal_type = signal_is_valid_crash(signal) ? SIG_TYPE_CRASH : SIG_TYPE_INVALID; + } else { + _E("Unknown signal name"); + free(ctx); + return NULL; + } + + ctx->signal = signal; + return ctx; +} + +STATIC struct _diagnostics_data_s *diagnostics_data_create(int fd) +{ + RETV_IF(fd < 0, NULL); + + struct _diagnostics_data_s *data; + + data = calloc(1, sizeof(struct _diagnostics_data_s)); + if (!data) { + _E("Unable to allocate memory"); + return NULL; + } + + data->fd = fd; + return data; +} + +STATIC void signal_handler(GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + struct _diagnostics_ctx_s *ctx; + struct dbus_signal_s *signal; + + _D("signal_handler"); + _D("parameters: %s", g_variant_print(parameters, TRUE)); + + if (!cb_info.cb) { + _E("No user cb set"); + return; + } + + _D("dbus_signal_create"); + signal = dbus_signal_create(sender_name, + object_path, + interface_name, + signal_name, + parameters); + if (!signal) { + _E("Unable to create signal structure"); + return; + } + + _D("diagnostics_create"); + ctx = diagnostics_create(signal); + if (!ctx) { + _E("Unable to create diagnostics context"); + dbus_signal_cleanup(signal); + return; + } + + _D("Fireing user cb!"); + cb_info.cb(ctx, cb_info.user_data); +} + +int diagnostics_set_notification_cb(diagnostics_notification_cb callback, void *user_data) +{ + RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported"); + RETV_IF(callback == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + int ret; + + _D("diagnostics_set_notification_cb()"); + + if (cb_info.cb) { + _E("Callback has already been set"); + return DIAGNOSTICS_ERROR_RESOURCE_BUSY; + } + + ret = dbus_subscribe(signal_handler); + if (ret) { + _E("Unable to subscribe to dbus signals"); + return DIAGNOSTICS_ERROR_IO_ERROR; + } + + cb_info.cb = callback; + cb_info.user_data = user_data; + + return DIAGNOSTICS_ERROR_NONE; +} + +int diagnostics_unset_notification_cb(void) +{ + RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported"); + + _D("diagnostics_unset_notification_cb()"); + + cb_info.cb = NULL; + cb_info.user_data = NULL; + + dbus_unsubscribe(); + return DIAGNOSTICS_ERROR_NONE; +} + +int diagnostics_request_client_data(const char *client_id, const char **params, int params_size, diagnostics_data_h *data) +{ + RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported"); + RETV_IF(client_id == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(params_size < 0, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + int fd = -1; + int ret; + + ret = dumpsys_dump(client_id, params_size, params, &fd); + if (ret != DIAGNOSTICS_ERROR_NONE) { + _E("dumpsys_dump() failed: %d", ret); + if (ret == TIZEN_ERROR_PERMISSION_DENIED) + return DIAGNOSTICS_ERROR_PERMISSION_DENIED; + return DIAGNOSTICS_ERROR_IO_ERROR; + } + + *data = diagnostics_data_create(fd); + if (*data == NULL) { + _E("Unable to create diagnostics_data"); + return DIAGNOSTICS_ERROR_OUT_OF_MEMORY; + } + + return DIAGNOSTICS_ERROR_NONE; +} + +int diagnostics_data_read(diagnostics_data_h data, void *buf, size_t count, int timeout_ms, size_t *bytes_read) +{ + RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported"); + RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(buf == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(bytes_read == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + struct pollfd poll_fd; + int ready; + int ret; + + poll_fd.fd = ((struct _diagnostics_data_s *)data)->fd; + poll_fd.events = POLLIN; + + ready = poll(&poll_fd, 1, timeout_ms <= 0 ? -1 : timeout_ms); + + if (ready == 0) { + _E("poll() timeout"); + return DIAGNOSTICS_ERROR_TIMED_OUT; + } + + if (ready < 0) { + _E("poll() failed: %m"); + return DIAGNOSTICS_ERROR_IO_ERROR; + } + + if (poll_fd.revents & POLLIN) { + ret = read(poll_fd.fd, buf, count); + if (ret < 0) { + _E("read() failed: %m, fd: %d", poll_fd.fd); + if (errno == EAGAIN) + return DIAGNOSTICS_ERROR_TRY_AGAIN; + return DIAGNOSTICS_ERROR_IO_ERROR; + } + *bytes_read = ret; + return DIAGNOSTICS_ERROR_NONE; + } else if (poll_fd.revents & POLLHUP) { + *bytes_read = 0; + return DIAGNOSTICS_ERROR_NONE; + } + + _E("received event: %d", poll_fd.revents); + return DIAGNOSTICS_ERROR_IO_ERROR; +} + +int diagnostics_get_client_id(diagnostics_ctx_h ctx, char **client_id) +{ + RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported"); + RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(client_id == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + *client_id = strdup(((struct _diagnostics_ctx_s *)ctx)->client_id); + + return DIAGNOSTICS_ERROR_NONE; +} + +STATIC int get_report_path(diagnostics_ctx_h ctx, const char **path, unsigned int *len) +{ + RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(path == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(((struct _diagnostics_ctx_s *)ctx)->signal_type != SIG_TYPE_CRASH, DIAGNOSTICS_ERROR_NOT_SUPPORTED); + + GVariant *val; + + val = g_variant_get_child_value(((struct _diagnostics_ctx_s *)ctx)->signal->parameters, SIG_CRASH_REPORTPATH); + *path = g_variant_get_string(val, len); + + return DIAGNOSTICS_ERROR_NONE; +} + +int diagnostics_get_data(diagnostics_ctx_h ctx, const char **params, int params_size, diagnostics_data_h *data) +{ + RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported"); + RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(params_size < 0, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + RETV_IF(((struct _diagnostics_ctx_s *)ctx)->signal_type != SIG_TYPE_CRASH, DIAGNOSTICS_ERROR_NOT_SUPPORTED); + + const char *report_path; + unsigned int len; + int report_id; + int fd; + int ret; + + /* + * TODO: Make this suitable for supporting other clients, not just crash-worker + */ + + ret = get_report_path(((struct _diagnostics_ctx_s *)ctx), &report_path, &len); + if (ret) { + _E("diagnostics_get_report_path() failed: %d", ret); + return DIAGNOSTICS_ERROR_IO_ERROR; + } + + if (params_size < 1) + return DIAGNOSTICS_ERROR_NOT_SUPPORTED; + + if (strcmp(params[0], "cs_full") == 0) + report_id = 0; + else if (strcmp(params[0], "cs_info_json") == 0) + report_id = 1; + else { + _E("Unsupported parameter: %s", params[0]); + return DIAGNOSTICS_ERROR_NOT_SUPPORTED; + } + + ret = dbus_get_file_from_report(report_path, report_id, &fd); + if (ret) { + _E("dbus_get_file_from_report() failed: %d", ret); + if (ret == -EACCES) + return DIAGNOSTICS_ERROR_PERMISSION_DENIED; + return DIAGNOSTICS_ERROR_IO_ERROR; + } + + *data = diagnostics_data_create(fd); + if (*data == NULL) { + _E("Unable to create diagnostics_data"); + close(fd); + return DIAGNOSTICS_ERROR_OUT_OF_MEMORY; + } + + return DIAGNOSTICS_ERROR_NONE; +} + +void diagnostics_destroy(diagnostics_ctx_h ctx) { + RET_IF(ctx == NULL); + + dbus_signal_cleanup(((struct _diagnostics_ctx_s *)ctx)->signal); + free(ctx); +} + +void diagnostics_data_destroy(diagnostics_data_h data) { + RET_IF(data == NULL); + + close(((struct _diagnostics_data_s *)data)->fd); + free(data); +} diff --git a/src/library/diagnostics.pc.in b/src/library/diagnostics.pc.in new file mode 100644 index 0000000..aacf5ce --- /dev/null +++ b/src/library/diagnostics.pc.in @@ -0,0 +1,13 @@ +# Package Information for pkg-config + +prefix=@PC_PREFIX@ +exec_prefix=/usr +libdir=@PC_LIBDIR@ +includedir=@PC_INCLUDE@ + +Name: @PC_NAME@ +Description: @PC_DESCRIPTION@ +Version: @VERSION@ +Requires: @PC_REQUIRED@ +Libs: -L${libdir} @PC_LDFLAGS@ +Cflags: -I${includedir} diff --git a/src/library/log.h b/src/library/log.h new file mode 100644 index 0000000..b633e30 --- /dev/null +++ b/src/library/log.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __DIAGNOSTICS_LOG_H__ +#define __DIAGNOSTICS_LOG_H__ + +#include + +#undef LOG_TAG +#define LOG_TAG "DIAGNOSTICS" + +#define _E(fmt, arg...) LOGE(fmt, ##arg) +#define _D(fmt, arg...) LOGD(fmt, ##arg) +#define _W(fmt, arg...) LOGW(fmt, ##arg) + +#define RET_IF(expr) \ + do { \ + if (expr) { \ + _E("(%s)", #expr); \ + return; \ + } \ + } while (0) +#define RETV_IF(expr, val) \ + do {\ + if (expr) { \ + _E("(%s)", #expr); \ + return (val); \ + } \ + } while (0) +#define RETM_IF(expr, fmt, arg...) \ + do {\ + if (expr) { \ + _E(fmt, ##arg); \ + return; \ + } \ + } while (0) +#define RETVM_IF(expr, val, fmt, arg...) \ + do {\ + if (expr) { \ + _E(fmt, ##arg); \ + return (val); \ + } \ + } while (0) + +#endif /* __DIAGNOSTICS_LOG_H__ */ diff --git a/src/library/signal.c b/src/library/signal.c new file mode 100644 index 0000000..78ece6e --- /dev/null +++ b/src/library/signal.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include "signal.h" +#include "log.h" +#include "dbus.h" + +#include +#include + +int signal_is_valid_crash(struct dbus_signal_s *signal) +{ + GVariant *extra; + + RETV_IF(signal == NULL, FALSE); + RETV_IF(g_variant_n_children(signal->parameters) != SIG_CRASH_SIZE, FALSE); + + extra = g_variant_get_child_value(signal->parameters, SIG_CRASH_EX); + RETV_IF(g_variant_is_container(extra) == FALSE, FALSE); + + return TRUE; +} diff --git a/src/library/signal.h b/src/library/signal.h new file mode 100644 index 0000000..98221af --- /dev/null +++ b/src/library/signal.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __SIGNAL_H__ +#define __SIGNAL_H__ + +#define SIG_CRASH_SIZE 8 +#define SIG_CRASH_CMDNAME 0 +#define SIG_CRASH_CMDPATH 1 +#define SIG_CRASH_APPID 2 +#define SIG_CRASH_PKGID 3 +#define SIG_CRASH_REPORTPATH 4 +#define SIG_CRASH_PID 5 +#define SIG_CRASH_TID 6 +#define SIG_CRASH_EX 7 +#define SIG_CRASH_EX_SYSSIGNAL "sys.signal" +#define SIG_CRASH_EX_SYSTIDCOMM "sys.tid.comm" +#define SIG_CRASH_EX_ARMPC "arm.pc" +#define SIG_CRASH_EX_ARMLR "arm.lr" + +struct dbus_signal_s; + +typedef enum { + SIG_TYPE_CRASH, + /* SIG_TYPE_CRASH_LEGACY ? */ + SIG_TYPE_INVALID +}signal_type_e; + +int signal_is_valid_crash(struct dbus_signal_s *signal); + +#endif /* __SIGNAL_H__ */ diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 0000000..1bd24f8 --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,99 @@ +# ADD_DEFINITIONS(-g -Werror -Wall -Wextra) +ADD_CUSTOM_TARGET(ctest ALL make test WORKING_DIRECTORY ./ USES_TERMINAL) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/library) + +function(add_mocked_test name test_flags) + add_executable(test_${name} test_${name}.c ${ADD_MOCKED_TEST_SOURCES} + ${COMPILE_OPTIONS} ${DEFAULT_C_COMPILE_FLAGS} ${ADD_MOCKED_TEST_COMPILE_OPTIONS} + ${LINK_LIBRARIES} ${CMOCKA_LIBRARIES} ${ADD_MOCKED_TEST_LINK_LIBRARIES} + ${LINK_OPTIONS} ${ADD_MOCKED_TEST_LINK_OPTIONS}) + add_test(test_${name} ${CMAKE_CURRENT_BINARY_DIR}/test_${name}) + target_link_libraries(test_${name} cmocka ${target}_static ${pkgs_LDFLAGS} ${test_flags}) + if(ENABLE_COVERAGE) + target_link_libraries(test_${name} -fprofile-arcs -ftest-coverage -lgcov) + endif(ENABLE_COVERAGE) + ADD_DEPENDENCIES(ctest test_${name}) +endfunction(add_mocked_test) + +# register test files (source filename: test_[name].c) +add_mocked_test(diagnostics_set_notification_cb "-Wl,--wrap,dbus_subscribe,--wrap,system_info_get_platform_bool") +add_mocked_test(diagnostics_request_client_data "-Wl,--wrap,dumpsys_dump") +add_mocked_test(diagnostics_get_client_id "") +add_mocked_test(diagnostics_get_report_path "") +add_mocked_test(diagnostics_get_data "-Wl,--wrap,dbus_get_file_from_report") +add_mocked_test(diagnostics_data_read "-Wl,--wrap,poll,--wrap,read") +add_mocked_test(diagnostics_create "") +add_mocked_test(diagnostics_data_create "") +add_mocked_test(diagnostics_signal_handler "") +add_mocked_test(diagnostics_destroy "") + +if(ENABLE_COVERAGE) +function(setup_coverage_target) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(COV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + find_program(GCOV_PATH gcov) + find_program(LCOV_PATH NAMES lcov) + find_program(GENHTML_PATH NAMES genhtml) + + if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") + endif() + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + if(${COV_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${COV_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${COV_EXCLUDE} ${COVERAGE_EXCLUDES}) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Setup target + add_custom_target(${COV_NAME} + + COMMAND ${LCOV_PATH} ${COV_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters + + COMMAND ${COV_EXECUTABLE} ${COV_EXECUTABLE_ARGS} + + COMMAND ${LCOV_PATH} ${COV_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${COV_NAME}.total + + COMMAND ${LCOV_PATH} ${COV_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${COV_NAME}.total ${LCOV_EXCLUDES} --output-file ${COV_NAME}.info + + COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${COV_GENHTML_ARGS} -o ${COV_NAME} ${COV_NAME}.info + + BYPRODUCTS + ${COV_NAME}.total + ${COV_NAME}.info + ${COV_NAME} + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + + DEPENDS ${COV_DEPENDENCIES} + ) + +endfunction() # setup_coverage_target + +setup_coverage_target( + NAME coverage + EXECUTABLE make test + EXCLUDE "src/test/*" +) + +add_custom_command(TARGET ctest POST_BUILD COMMAND make coverage) + +endif(ENABLE_COVERAGE) diff --git a/src/test/tct-support/.gitignore b/src/test/tct-support/.gitignore new file mode 100644 index 0000000..e10e727 --- /dev/null +++ b/src/test/tct-support/.gitignore @@ -0,0 +1 @@ +/.metadata/ diff --git a/src/test/tct-support/README.md b/src/test/tct-support/README.md new file mode 100644 index 0000000..bc372dc --- /dev/null +++ b/src/test/tct-support/README.md @@ -0,0 +1,19 @@ +# Bugreport TCT helper apps + +## Build: + 1. Open Tizen Studio specifying ./tct-support directory as workspace + 2. Open projects from filesystem + 3. Right click on each project and choose "Build signed package" + +**NOTE**: Before build, open Certificate Manager and add security profile with certificates from `platform/core/security/hash-signer`. + +**NOTE2**: Target architecture can be changed under: Project properties -> C/C++ Build -> Tizen Settings. + +## Deploy: + +Copy `./tct-support//Debug/*.tpk` packages to Native TCT repo (`test/tct/native/api`): + +- src/utc/diagnostics/res/mobile +- src/utc/diagnostics/res/wearable +- src/utc/diagnostics/res/tv +- src/utc/diagnostics/res/tizeniot \ No newline at end of file diff --git a/src/test/tct-support/crash-test/.cproject b/src/test/tct-support/crash-test/.cproject new file mode 100644 index 0000000..f772011 --- /dev/null +++ b/src/test/tct-support/crash-test/.cproject @@ -0,0 +1,660 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/tct-support/crash-test/.exportMap b/src/test/tct-support/crash-test/.exportMap new file mode 100644 index 0000000..de30516 --- /dev/null +++ b/src/test/tct-support/crash-test/.exportMap @@ -0,0 +1,5 @@ +{ + global: main; + _IO_*; + local: *; +}; diff --git a/src/test/tct-support/crash-test/.gitignore b/src/test/tct-support/crash-test/.gitignore new file mode 100644 index 0000000..5c890a5 --- /dev/null +++ b/src/test/tct-support/crash-test/.gitignore @@ -0,0 +1,3 @@ +/Debug/ +/SA_Report/ +/.sign/ \ No newline at end of file diff --git a/src/test/tct-support/crash-test/.package-stamp b/src/test/tct-support/crash-test/.package-stamp new file mode 100644 index 0000000..40cbe59 --- /dev/null +++ b/src/test/tct-support/crash-test/.package-stamp @@ -0,0 +1 @@ +TPK \ No newline at end of file diff --git a/src/test/tct-support/crash-test/.project b/src/test/tct-support/crash-test/.project new file mode 100644 index 0000000..c1fda0d --- /dev/null +++ b/src/test/tct-support/crash-test/.project @@ -0,0 +1,46 @@ + + + crash-test + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + 1594987985051 + + 26 + + org.eclipse.ui.ide.multiFilter + 1.0-projectRelativePath-matches-false-false-*/.tpk + + + + 1594987985053 + + 6 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-project_def.prop + + + + diff --git a/src/test/tct-support/crash-test/.tproject b/src/test/tct-support/crash-test/.tproject new file mode 100644 index 0000000..9138ab2 --- /dev/null +++ b/src/test/tct-support/crash-test/.tproject @@ -0,0 +1,12 @@ + + + + + mobile-6.0 + + + + + + + diff --git a/src/test/tct-support/crash-test/inc/crash-test.h b/src/test/tct-support/crash-test/inc/crash-test.h new file mode 100644 index 0000000..3d35143 --- /dev/null +++ b/src/test/tct-support/crash-test/inc/crash-test.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __CRASH_TEST_H__ +#define __CRASH_TEST_H__ + +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "crash-test" + + +#endif /* __CRASH_TEST_H__ */ diff --git a/src/test/tct-support/crash-test/project_def.prop b/src/test/tct-support/crash-test/project_def.prop new file mode 100644 index 0000000..75b81b9 --- /dev/null +++ b/src/test/tct-support/crash-test/project_def.prop @@ -0,0 +1,11 @@ +APPNAME = crash-test + +type = app +profile = mobile-6.0 + +USER_SRCS = src/crash-test.c +USER_DEFS = +USER_INC_DIRS = inc +USER_OBJS = +USER_LIBS = +USER_EDCS = diff --git a/src/test/tct-support/crash-test/shared/res/crash-test.png b/src/test/tct-support/crash-test/shared/res/crash-test.png new file mode 100644 index 0000000000000000000000000000000000000000..9765b1bda7e5bddf0925555ab204b887a873bf24 GIT binary patch literal 57662 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 kZ}gYfNaCF}xV$|1>H6z0+kD +#include +#include +#include "crash-test.h" + +bool service_app_create(void *data) +{ + raise(SIGSEGV); + return true; +} + +void service_app_terminate(void *data) +{ + // Todo: add your code here. + return; +} + +void service_app_control(app_control_h app_control, void *data) +{ + // Todo: add your code here. + return; +} + +static void +service_app_lang_changed(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_LANGUAGE_CHANGED*/ + return; +} + +static void +service_app_region_changed(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_REGION_FORMAT_CHANGED*/ +} + +static void +service_app_low_battery(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_LOW_BATTERY*/ +} + +static void +service_app_low_memory(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_LOW_MEMORY*/ +} + +int main(int argc, char* argv[]) +{ + char ad[50] = {0,}; + service_app_lifecycle_callback_s event_callback; + app_event_handler_h handlers[5] = {NULL, }; + + event_callback.create = service_app_create; + event_callback.terminate = service_app_terminate; + event_callback.app_control = service_app_control; + + service_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, service_app_low_battery, &ad); + service_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, service_app_low_memory, &ad); + service_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, service_app_lang_changed, &ad); + service_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, service_app_region_changed, &ad); + + return service_app_main(argc, argv, &event_callback, ad); +} diff --git a/src/test/tct-support/crash-test/tizen-manifest.xml b/src/test/tct-support/crash-test/tizen-manifest.xml new file mode 100644 index 0000000..8412f48 --- /dev/null +++ b/src/test/tct-support/crash-test/tizen-manifest.xml @@ -0,0 +1,8 @@ + + + + + crash-test.png + + + diff --git a/src/test/tct-support/dumpsys-client/.cproject b/src/test/tct-support/dumpsys-client/.cproject new file mode 100644 index 0000000..e2cce8e --- /dev/null +++ b/src/test/tct-support/dumpsys-client/.cproject @@ -0,0 +1,663 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/tct-support/dumpsys-client/.exportMap b/src/test/tct-support/dumpsys-client/.exportMap new file mode 100644 index 0000000..de30516 --- /dev/null +++ b/src/test/tct-support/dumpsys-client/.exportMap @@ -0,0 +1,5 @@ +{ + global: main; + _IO_*; + local: *; +}; diff --git a/src/test/tct-support/dumpsys-client/.gitignore b/src/test/tct-support/dumpsys-client/.gitignore new file mode 100644 index 0000000..5c890a5 --- /dev/null +++ b/src/test/tct-support/dumpsys-client/.gitignore @@ -0,0 +1,3 @@ +/Debug/ +/SA_Report/ +/.sign/ \ No newline at end of file diff --git a/src/test/tct-support/dumpsys-client/.package-stamp b/src/test/tct-support/dumpsys-client/.package-stamp new file mode 100644 index 0000000..40cbe59 --- /dev/null +++ b/src/test/tct-support/dumpsys-client/.package-stamp @@ -0,0 +1 @@ +TPK \ No newline at end of file diff --git a/src/test/tct-support/dumpsys-client/.project b/src/test/tct-support/dumpsys-client/.project new file mode 100644 index 0000000..e23e80f --- /dev/null +++ b/src/test/tct-support/dumpsys-client/.project @@ -0,0 +1,46 @@ + + + dumpsys-client + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + 1594987799141 + + 26 + + org.eclipse.ui.ide.multiFilter + 1.0-projectRelativePath-matches-false-false-*/.tpk + + + + 1594987799671 + + 6 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-project_def.prop + + + + diff --git a/src/test/tct-support/dumpsys-client/.tproject b/src/test/tct-support/dumpsys-client/.tproject new file mode 100644 index 0000000..9138ab2 --- /dev/null +++ b/src/test/tct-support/dumpsys-client/.tproject @@ -0,0 +1,12 @@ + + + + + mobile-6.0 + + + + + + + diff --git a/src/test/tct-support/dumpsys-client/inc/dumpsys-client.h b/src/test/tct-support/dumpsys-client/inc/dumpsys-client.h new file mode 100644 index 0000000..035d4bd --- /dev/null +++ b/src/test/tct-support/dumpsys-client/inc/dumpsys-client.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __DUMPSYS_CLIENT_H__ +#define __DUMPSYS_CLIENT_H__ + +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "dumpsys-client" + + +#endif /* __DUMPSYS_CLIENT_H__ */ diff --git a/src/test/tct-support/dumpsys-client/project_def.prop b/src/test/tct-support/dumpsys-client/project_def.prop new file mode 100644 index 0000000..0a62558 --- /dev/null +++ b/src/test/tct-support/dumpsys-client/project_def.prop @@ -0,0 +1,11 @@ +APPNAME = dumpsys-client + +type = app +profile = mobile-6.0 + +USER_SRCS = src/dumpsys-client.c +USER_DEFS = +USER_INC_DIRS = inc +USER_OBJS = +USER_LIBS = +USER_EDCS = diff --git a/src/test/tct-support/dumpsys-client/shared/res/dumpsys-client.png b/src/test/tct-support/dumpsys-client/shared/res/dumpsys-client.png new file mode 100644 index 0000000000000000000000000000000000000000..9765b1bda7e5bddf0925555ab204b887a873bf24 GIT binary patch literal 57662 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 kZ}gYfNaCF}xV$|1>H6z0+kD +#include +#include +#include +#include +#include "dumpsys-client.h" + +#define LOGD(...) dlog_print(DLOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define LOGE(...) dlog_print(DLOG_ERROR, LOG_TAG, __VA_ARGS__) + +static int cb(dumpsys_dump_h ctx) +{ + int argc; + char **argv; + int ret; + + LOGD("Dumpsys dump callback"); + + ret = dumpsys_get_args_count(ctx, &argc); + if (ret) { + LOGE("dumpsys_get_args_count() failed: %d", ret); + return -1; + } + + LOGD("argc: %d", argc); + + ret = dumpsys_get_args_array(ctx, &argv); + if (ret) { + LOGE("dumpsys_get_args_array() failed: %d", ret); + return -1; + } + + for (int i=0; i < argc; i++) + LOGD("arg %d: %s", i, argv[i]); + + if (argc < 2) { + LOGE("Not enough dump arguments"); + LOGD("Dump request must be called with two arguments: {n, s}"); + LOGD("Dump log will consist of string 's' repeated 'n' times"); + return -1; + } + + LOGD("Writing data..."); + for (int i=0; i < atoi(argv[0]); i++) { + ret = dumpsys_write(ctx, argv[1], strlen(argv[1])); + if (ret) { + LOGE("dumpsys_write() failed: %d", ret); + return -1; + } + } + + LOGD("Done"); + + return 0; +} + +bool service_app_create(void *data) +{ + LOGD("app_create()"); + int ret; + void *context; + + ret = dumpsys_register_dump_cb(cb, &context); + if (ret) { + LOGE("dumpsys_register_dump_cb() failed: %d", ret); + }; + + return true; +} + +void service_app_terminate(void *data) +{ + // Todo: add your code here. + return; +} + +void service_app_control(app_control_h app_control, void *data) +{ + // Todo: add your code here. + return; +} + +static void +service_app_lang_changed(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_LANGUAGE_CHANGED*/ + return; +} + +static void +service_app_region_changed(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_REGION_FORMAT_CHANGED*/ +} + +static void +service_app_low_battery(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_LOW_BATTERY*/ +} + +static void +service_app_low_memory(app_event_info_h event_info, void *user_data) +{ + /*APP_EVENT_LOW_MEMORY*/ +} + +int main(int argc, char* argv[]) +{ + char ad[50] = {0,}; + service_app_lifecycle_callback_s event_callback; + app_event_handler_h handlers[5] = {NULL, }; + + event_callback.create = service_app_create; + event_callback.terminate = service_app_terminate; + event_callback.app_control = service_app_control; + + service_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, service_app_low_battery, &ad); + service_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, service_app_low_memory, &ad); + service_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, service_app_lang_changed, &ad); + service_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, service_app_region_changed, &ad); + + return service_app_main(argc, argv, &event_callback, ad); +} diff --git a/src/test/tct-support/dumpsys-client/tizen-manifest.xml b/src/test/tct-support/dumpsys-client/tizen-manifest.xml new file mode 100644 index 0000000..81d6dd9 --- /dev/null +++ b/src/test/tct-support/dumpsys-client/tizen-manifest.xml @@ -0,0 +1,8 @@ + + + + + dumpsys-client.png + + + diff --git a/src/test/test_diagnostics.h b/src/test/test_diagnostics.h new file mode 100644 index 0000000..ec32473 --- /dev/null +++ b/src/test/test_diagnostics.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __TIZEN_TEST_DIAGNOSTICS_H__ +#define __TIZEN_TEST_DIAGNOSTICS_H__ + +#include +#include + +#include "signal.h" +#include "dbus.h" + +#define DBUS_SIGNAL_CRASH_APPID "org.tizen.multi-assistant-service" +#define DBUS_SIGNAL_ABNOR_APPID "top" +#define DBUS_SIGNAL_CRASH_COMM "org.tizen.multi" +#define DBUS_SIGNAL_CRASH_EXEC_PATH "/usr/apps/org.tizen.multi-assistant-service/bin/org.tizen.multi-assistant-service" +#define DBUS_SIGNAL_CRASH_PID 1589 +#define DBUS_SIGNAL_ABNOR_PID 10076 +#define DBUS_SIGNAL_CRASH_REPORT_PATH "/opt/usr/share/crash/dump//org.tizen.multi-assistant-service_1589_20190101181719.zip" +#define DBUS_SIGNAL_ABNOR_REPORT_PATH "/opt/usr/share/crash/livedump//top_10076_20190101183649.zip" +#define DBUS_SIGNAL_CRASH_SIGNAL 6 +#define DBUS_SIGNAL_ABNOR_IS_PROC_DEAD FALSE + +#ifdef __cplusplus +extern "C" { +#endif + +struct _diagnostics_ctx_s { + char *client_id; + struct dbus_signal_s *signal; + signal_type_e signal_type; +}; + +struct _diagnostics_data_s { + int fd; +}; + +int system_info_get_platform_bool(const char *key, bool *value) +{ + *value = true; + return SYSTEM_INFO_ERROR_NONE; +} + +struct _diagnostics_ctx_s *build_ctx_crash() +{ + struct _diagnostics_ctx_s *ctx = calloc(1, sizeof(struct _diagnostics_ctx_s)); + ctx->signal = calloc(1, sizeof(struct dbus_signal_s)); + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE("(sssssiia{sv})")); + g_variant_builder_add(&builder, "s", "org.tizen.multi-assistant-xxx"); + g_variant_builder_add(&builder, "s", DBUS_SIGNAL_CRASH_EXEC_PATH); + g_variant_builder_add(&builder, "s", DBUS_SIGNAL_CRASH_APPID); + g_variant_builder_add(&builder, "s", "org.tizen.multi-yyy-xxx"); + g_variant_builder_add(&builder, "s", DBUS_SIGNAL_CRASH_REPORT_PATH); + g_variant_builder_add(&builder, "i", DBUS_SIGNAL_CRASH_PID); + g_variant_builder_add(&builder, "i", 1589); + g_variant_builder_open(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&builder, "{sv}", SIG_CRASH_EX_SYSSIGNAL, g_variant_new_int32(DBUS_SIGNAL_CRASH_SIGNAL)); + g_variant_builder_add(&builder, "{sv}", SIG_CRASH_EX_SYSTIDCOMM, g_variant_new_string(DBUS_SIGNAL_CRASH_COMM)); + g_variant_builder_add(&builder, "{sv}", SIG_CRASH_EX_ARMPC, g_variant_new_int32(3055767608)); + g_variant_builder_add(&builder, "{sv}", SIG_CRASH_EX_ARMLR, g_variant_new_int32(0)); + g_variant_builder_close(&builder); + ctx->signal->parameters = g_variant_builder_end(&builder); + ctx->signal->signal_name = g_strdup(DBUS_MEMBER_CRASH); + ctx->signal->sender_name = g_strdup(DBUS_SENDER_CRASH); + ctx->signal_type = SIG_TYPE_CRASH; + + ctx->client_id = ctx->signal->sender_name; + + return ctx; +} + +void destroy_ctx(struct _diagnostics_ctx_s *ctx) +{ + g_variant_unref(ctx->signal->parameters); + dbus_signal_cleanup(ctx->signal); + free(ctx); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __TIZEN_TEST_DIAGNOSTICS_H__ */ diff --git a/src/test/test_diagnostics_add_function_defs.h b/src/test/test_diagnostics_add_function_defs.h new file mode 100644 index 0000000..e4a65b2 --- /dev/null +++ b/src/test/test_diagnostics_add_function_defs.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __TIZEN_TEST_DIAGNOSTICS_ADD_FUNCTION_DEFS_H__ +#define __TIZEN_TEST_DIAGNOSTICS_ADD_FUNCTION_DEFS_H__ + +#include "diagnostics.h" + +#include "dbus.h" + +#define STATIC + +#ifdef __cplusplus +extern "C" { +# endif + +extern struct _diagnostics_cb_info_s cb_info; + +int get_report_path(void *ctx, const char **path, unsigned int *len); +struct _diagnostics_ctx_s *diagnostics_create(struct dbus_signal_s *signal); +struct _diagnostics_data_s *diagnostics_data_create(int fd); +void signal_handler(GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* __TIZEN_TEST_BUGREPORT_ADD_FUNCTION_DEFS_H__ */ diff --git a/src/test/test_diagnostics_create.c b/src/test/test_diagnostics_create.c new file mode 100644 index 0000000..7b6d84d --- /dev/null +++ b/src/test/test_diagnostics_create.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include + +#include "test_diagnostics.h" +#include "test_diagnostics_add_function_defs.h" + +static void test_diagnostics_create_n1(void **state) { + (void) state; + struct dbus_signal_s *signal = NULL; + + assert_null(diagnostics_create(signal)); +} + +static void test_diagnostics_create_n2(void **state) { + (void) state; + struct _diagnostics_ctx_s *ctx = build_ctx_crash(); + free(ctx->signal->signal_name); + ctx->signal->signal_name = g_strdup("non dbus member crash"); + struct _diagnostics_ctx_s *result = diagnostics_create(ctx->signal); + + assert_null(result); + + destroy_ctx(ctx); +} + +static void test_diagnostics_create_n3(void **state) { + (void) state; + struct _diagnostics_ctx_s *ctx = build_ctx_crash(); + free(ctx->signal->signal_name); + ctx->signal->signal_name = NULL; + struct _diagnostics_ctx_s *result = diagnostics_create(ctx->signal); + + assert_null(result); + + destroy_ctx(ctx); +} + +static void test_diagnostics_create_p1(void **state) { + (void) state; + struct _diagnostics_ctx_s *ctx = build_ctx_crash(); + struct _diagnostics_ctx_s *result = diagnostics_create(ctx->signal); + + assert_non_null(result); + assert_string_equal(result->client_id, DBUS_SENDER_CRASH); + assert_ptr_equal(result->signal, ctx->signal); + assert_int_equal(result->signal_type, SIG_TYPE_CRASH); + + destroy_ctx(ctx); + free(result); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_create_n1), + cmocka_unit_test(test_diagnostics_create_n2), + cmocka_unit_test(test_diagnostics_create_n3), + cmocka_unit_test(test_diagnostics_create_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_data_create.c b/src/test/test_diagnostics_data_create.c new file mode 100644 index 0000000..5154d2d --- /dev/null +++ b/src/test/test_diagnostics_data_create.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include + +#include "test_diagnostics.h" +#include "test_diagnostics_add_function_defs.h" + +static void test_diagnostics_data_create_n1(void **state) { + (void) state; + int fd = -1; + + assert_null(diagnostics_data_create(fd)); +} + +static void test_diagnostics_data_create_p1(void **state) { + (void) state; + int fd = 0; + struct _diagnostics_data_s *result = diagnostics_data_create(fd); + + assert_non_null(result); + assert_int_equal(result->fd, fd); + + free(result); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_data_create_n1), + cmocka_unit_test(test_diagnostics_data_create_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_data_read.c b/src/test/test_diagnostics_data_read.c new file mode 100644 index 0000000..1562817 --- /dev/null +++ b/src/test/test_diagnostics_data_read.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "signal.h" +#include "dbus.h" + +#include +#include + +#include "test_diagnostics.h" + +static void test_diagnostics_data_read_n1(void **state) { + (void) state; + char buf[256]; + size_t count = sizeof(buf); + int timeout = 100; + size_t bytes_read = 0; + + assert_int_equal(diagnostics_data_read(NULL, buf, count, timeout, &bytes_read), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_data_read_n2(void **state) { + (void) state; + struct _diagnostics_data_s data; + char buf[256]; + size_t count = sizeof(buf); + int timeout = 100; + size_t bytes_read = 0; + + assert_int_equal(diagnostics_data_read(&data, NULL, count, timeout, &bytes_read), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_data_read_n4(void **state) { + (void) state; + struct _diagnostics_data_s data; + char buf[256]; + size_t count = sizeof(buf); + int timeout = 100; + + assert_int_equal(diagnostics_data_read(&data, buf, count, timeout, NULL), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout) +{ + (void) __fds; + (void) __nfds; + (void) __timeout; + + __fds->revents |= (1< +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "signal.h" +#include "dbus.h" + +#include + +#include "test_diagnostics.h" + +static void test_diagnostics_destroy_n1(void **state) { + (void) state; + diagnostics_ctx_h ctx = NULL; + diagnostics_destroy(ctx); + + assert_null(ctx); +} + +static void test_diagnostics_data_destroy_n1(void **state) { + (void) state; + diagnostics_data_h data = NULL; + diagnostics_data_destroy(data); + + assert_null(data); +} + +static void test_diagnostics_destroy_p1(void **state) { + (void) state; + diagnostics_ctx_h ctx = build_ctx_crash(); + + diagnostics_destroy(ctx); +} + +static void test_diagnostics_data_destroy_p2(void **state) { + (void) state; + diagnostics_data_h data = calloc(1, sizeof(struct _diagnostics_data_s)); + + diagnostics_data_destroy(data); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_destroy_n1), + cmocka_unit_test(test_diagnostics_data_destroy_n1), + cmocka_unit_test(test_diagnostics_destroy_p1), + cmocka_unit_test(test_diagnostics_data_destroy_p2), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_get_client_id.c b/src/test/test_diagnostics_get_client_id.c new file mode 100644 index 0000000..dd0e96a --- /dev/null +++ b/src/test/test_diagnostics_get_client_id.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "signal.h" +#include "dbus.h" + +#include "test_diagnostics.h" + +static void test_diagnostics_get_client_id_n1(void **state) { + (void) state; + char *client_id; + + assert_int_equal(diagnostics_get_client_id(NULL, &client_id), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_get_client_id_n2(void **state) { + (void) state; + struct _diagnostics_ctx_s *ctx = build_ctx_crash(); + + assert_int_equal(diagnostics_get_client_id(ctx, NULL), DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + destroy_ctx(ctx); +} + +static void test_diagnostics_get_client_id_p1(void **state) { + (void) state; + diagnostics_ctx_h ctx = build_ctx_crash(); + char *client_id; + int result = diagnostics_get_client_id(ctx, &client_id); + + assert_int_equal(result, DIAGNOSTICS_ERROR_NONE); + assert_string_equal(client_id, DBUS_SENDER_CRASH); + + destroy_ctx(ctx); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_get_client_id_n1), + cmocka_unit_test(test_diagnostics_get_client_id_n2), + cmocka_unit_test(test_diagnostics_get_client_id_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_get_data.c b/src/test/test_diagnostics_get_data.c new file mode 100644 index 0000000..f45c46a --- /dev/null +++ b/src/test/test_diagnostics_get_data.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "signal.h" +#include "dbus.h" + +#include + +#include "test_diagnostics.h" + +static void test_diagnostics_get_data_n1(void **state) { + (void) state; + diagnostics_data_h data; + const char *params[] = {"cs_full"}; + + assert_int_equal(diagnostics_get_data(NULL, params, 1, (void**)&data), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_get_data_n2(void **state) { + (void) state; + diagnostics_ctx_h br_ptr = build_ctx_crash(); + diagnostics_data_h data; + const char *params[] = {"cs_full"}; + + assert_int_equal(diagnostics_get_data(br_ptr, params, -1, (void**)&data), DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + destroy_ctx(br_ptr); +} + +static void test_diagnostics_get_data_n3(void **state) { + (void) state; + diagnostics_ctx_h br_ptr = build_ctx_crash(); + const char *params[] = {"cs_full"}; + + assert_int_equal(diagnostics_get_data(br_ptr, params, 1, NULL), DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + destroy_ctx(br_ptr); +} + +int dbus_get_file_from_report(const char *report_path, const int entry, int *fd) +{ + (void) report_path; + (void) entry; + *fd = 1; + + return (int)mock();; +} + +static void test_diagnostics_get_data_p1(void **state) { + (void) state; + struct _diagnostics_ctx_s *ctx = build_ctx_crash(); + struct _diagnostics_data_s *data = NULL; + const char *params[] = {"cs_full"}; + + will_return(dbus_get_file_from_report, 0); + + int result = diagnostics_get_data(ctx, params, 1, (void**)&data); + + assert_int_equal(result, DIAGNOSTICS_ERROR_NONE); + assert_non_null(data); + assert_int_equal(data->fd, 1); + + destroy_ctx(ctx); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_get_data_n1), + cmocka_unit_test(test_diagnostics_get_data_n2), + cmocka_unit_test(test_diagnostics_get_data_n3), + cmocka_unit_test(test_diagnostics_get_data_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_get_report_path.c b/src/test/test_diagnostics_get_report_path.c new file mode 100644 index 0000000..fad3f53 --- /dev/null +++ b/src/test/test_diagnostics_get_report_path.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "signal.h" +#include "dbus.h" + +#include + +#include "test_diagnostics.h" +#include "test_diagnostics_add_function_defs.h" + +static void test_diagnostics_get_report_path_n1(void **state) { + (void) state; + const char *path; + unsigned int len; + + assert_int_equal(get_report_path(NULL, &path, &len), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_get_report_path_n2(void **state) { + (void) state; + diagnostics_ctx_h br_ptr = build_ctx_crash(); + unsigned int len; + + assert_int_equal(get_report_path(br_ptr, NULL, &len), DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + destroy_ctx(br_ptr); +} + +static void test_diagnostics_get_report_path_n3(void **state) { + (void) state; + diagnostics_ctx_h ctx = build_ctx_crash(); + ((struct _diagnostics_ctx_s *)ctx)->signal_type = SIG_TYPE_CRASH + 10; + const char *path; + unsigned int len; + + assert_int_equal(get_report_path(ctx, &path, &len), DIAGNOSTICS_ERROR_NOT_SUPPORTED); + + destroy_ctx(ctx); +} + +static void test_diagnostics_get_report_path_p1(void **state) { + (void) state; + diagnostics_ctx_h ctx = build_ctx_crash(); + const char *path; + unsigned int len; + int result = get_report_path(ctx, &path, &len); + + assert_int_equal(result, DIAGNOSTICS_ERROR_NONE); + assert_string_equal(path, DBUS_SIGNAL_CRASH_REPORT_PATH); + destroy_ctx(ctx); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_get_report_path_n1), + cmocka_unit_test(test_diagnostics_get_report_path_n2), + cmocka_unit_test(test_diagnostics_get_report_path_n3), + cmocka_unit_test(test_diagnostics_get_report_path_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_request_client_data.c b/src/test/test_diagnostics_request_client_data.c new file mode 100644 index 0000000..f7df89f --- /dev/null +++ b/src/test/test_diagnostics_request_client_data.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "signal.h" +#include "dbus.h" + +#include + +#include "test_diagnostics.h" + +static void test_diagnostics_request_client_data_n1(void **state) { + (void) state; + int params_size = 3; + const char *params[] = {"a", "b", "c"}; + diagnostics_data_h data; + + assert_int_equal(diagnostics_request_client_data(NULL, params, params_size, (void**)&data), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_request_client_data_n2(void **state) { + (void) state; + const char *client_id = ""; + int params_size = -1; + const char *params[] = {"a", "b", "c"}; + diagnostics_data_h data; + + assert_int_equal(diagnostics_request_client_data(client_id, params, params_size, (void**)&data), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +static void test_diagnostics_request_client_data_n3(void **state) { + (void) state; + const char *client_id = ""; + int params_size = 0; + diagnostics_data_h data; + + will_return(dumpsys_dump, DIAGNOSTICS_ERROR_INVALID_PARAMETER); + + assert_int_equal(diagnostics_request_client_data(client_id, NULL, params_size, (void**)&data), DIAGNOSTICS_ERROR_IO_ERROR); +} + +static void test_diagnostics_request_client_data_n4(void **state) { + (void) state; + const char *client_id = ""; + int params_size = 3; + const char *params[] = {"a", "b", "c"}; + + assert_int_equal(diagnostics_request_client_data(client_id, params, params_size, NULL), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +int dumpsys_dump(const char *service_name, int argc, const char **argv, int *out_fd) +{ + (void) service_name; + (void) argc; + (void) argv; + (void) out_fd; + + *out_fd = 1; + + return (int)mock(); +} + +static void test_diagnostics_request_client_data_n5(void **state) { + (void) state; + const char *client_id = ""; + int params_size = 3; + const char *params[] = {"a", "b", "c"}; + diagnostics_data_h data = NULL; + + will_return(dumpsys_dump, TIZEN_ERROR_CONNECTION_REFUSED); + + int result = diagnostics_request_client_data(client_id, params, params_size, (void**)&data); + + assert_int_equal(result, DIAGNOSTICS_ERROR_IO_ERROR); + assert_null(data); +} + +static void test_diagnostics_request_client_data_p1(void **state) { + (void) state; + const char *client_id = ""; + int params_size = 3; + const char *params[] = {"a", "b", "c"}; + struct _diagnostics_data_s *data = NULL; + + will_return(dumpsys_dump, DIAGNOSTICS_ERROR_NONE); + + int result = diagnostics_request_client_data(client_id, params, params_size, (void**)&data); + + assert_int_equal(result, DIAGNOSTICS_ERROR_NONE); + assert_non_null(data); + assert_int_equal(data->fd, 1); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_request_client_data_n1), + cmocka_unit_test(test_diagnostics_request_client_data_n2), + cmocka_unit_test(test_diagnostics_request_client_data_n3), + cmocka_unit_test(test_diagnostics_request_client_data_n4), + cmocka_unit_test(test_diagnostics_request_client_data_n5), + cmocka_unit_test(test_diagnostics_request_client_data_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_set_notification_cb.c b/src/test/test_diagnostics_set_notification_cb.c new file mode 100644 index 0000000..3d6e12c --- /dev/null +++ b/src/test/test_diagnostics_set_notification_cb.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include "test_diagnostics.h" +#include "test_diagnostics_add_function_defs.h" + +struct _diagnostics_cb_info_s { + diagnostics_notification_cb cb; + void *user_data; +}; + +void callback(void *ctx, void *user_data) +{ + (void)ctx; + (void)user_data; +} + +static void test_diagnostics_set_notification_cb_n1(void **state) { + (void) state; + assert_int_equal(diagnostics_set_notification_cb(NULL, NULL), DIAGNOSTICS_ERROR_INVALID_PARAMETER); +} + +int dbus_subscribe(void (*signal_handler)(GDBusConnection *, + const gchar *, + const gchar *, + const gchar *, + const gchar *, + GVariant *, + gpointer)) +{ + (void) signal_handler; + return (int)mock(); +} + +static void test_diagnostics_set_notification_cb_p1(void **state) { + (void) state; + + will_return(dbus_subscribe, DIAGNOSTICS_ERROR_NONE); + + assert_int_equal(diagnostics_set_notification_cb(callback, NULL), DIAGNOSTICS_ERROR_NONE); + assert_ptr_equal(cb_info.cb, callback); + assert_ptr_equal(cb_info.user_data, NULL); + + cb_info.cb = NULL; +} + +static void test_diagnostics_set_notification_cb_p2(void **state) { + (void) state; + + will_return(dbus_subscribe, DIAGNOSTICS_ERROR_NONE); + + assert_int_equal(diagnostics_set_notification_cb(callback, NULL), DIAGNOSTICS_ERROR_NONE); + assert_int_equal(diagnostics_set_notification_cb(callback, NULL), DIAGNOSTICS_ERROR_RESOURCE_BUSY); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_set_notification_cb_n1), + cmocka_unit_test(test_diagnostics_set_notification_cb_p1), + cmocka_unit_test(test_diagnostics_set_notification_cb_p2), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/test/test_diagnostics_signal_handler.c b/src/test/test_diagnostics_signal_handler.c new file mode 100644 index 0000000..f6f588c --- /dev/null +++ b/src/test/test_diagnostics_signal_handler.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "diagnostics.h" +#include +#include + +#include + +#include "test_diagnostics.h" +#include "test_diagnostics_add_function_defs.h" + +struct _diagnostics_cb_info_s { + diagnostics_notification_cb cb; + void *user_data; +}; + +void signal_callback(void *_ctx, void *user_data) +{ + struct _diagnostics_ctx_s *ctx = _ctx; + + assert_string_equal(ctx->signal->sender_name, "sender"); + assert_string_equal(ctx->signal->object_path, "object path"); + assert_string_equal(ctx->signal->interface_name, "interface name"); + assert_string_equal(ctx->signal->signal_name, "signal name"); + assert_ptr_equal(user_data, NULL); + assert_null(ctx->signal->parameters); +} + +static void test_diagnostics_signal_handler_p1(void **state) { + (void) state; + const gchar *sender_name = g_strdup("sender"); + const gchar *object_path = g_strdup("object path"); + const gchar *interface_name = g_strdup("interface name"); + const gchar *signal_name = g_strdup("signal name"); + char *t = "abc"; + gboolean en = FALSE; + GVariant *parameters = g_variant_new_parsed ("{'title': <%s>, 'enabled': <%b>}", t, en); + cb_info.cb = signal_callback; + + signal_handler(NULL, sender_name, object_path, interface_name, signal_name, parameters, NULL); + + g_free((gchar*)sender_name); + g_free((gchar*)object_path); + g_free((gchar*)interface_name); + g_free((gchar*)signal_name); + g_variant_unref(parameters); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_diagnostics_signal_handler_p1), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} -- 2.34.1