1. Background Introduction
Many developers are quite intimidated when it comes to C++. Memory operations are one of the main aspects that cause such concerns. Unreasonable memory operations, such as array out-of-bounds access, memory leaks, and releasing already released addresses, may lead to program performance issues like high memory consumption and lagging. More seriously, they can even cause the program to crash. When an application encounters an error during its operation and the application process terminates, the application will throw an error log to notify the reason for the crash. Developers can analyze the cause of the application crash and the location of the code that triggered the crash by viewing the error log. FaultLog is automatically collected by the system from the device and includes the following types of fault information:
- App Freeze
- CPP Crash
- JS Crash
- System Freeze
- ASan
- TSan
We won't go into too much detail about other tools as they are commonly used in daily work. Since ArkTS is single-threaded and the multi-threading mechanism it provides is based on non-shared memory, many multi-threading scenarios may be implemented at the C++ layer. Therefore, this article mainly introduces the memory debugging and thread debugging tools provided by DevEco Studio: ASan detection and TSan detection.
2. ASan Detection
The main memory issues in C++ are manifested in out-of-bounds access, failure to release memory, and releasing memory that has already been released. For performance reasons, compilers and runtime frameworks do not perform security checks on memory operations. DevEco Studio provides ASan to help developers detect out-of-bounds address problems. ASan, short for Address-Sanitizer, will first be introduced in terms of how it is configured in a project.
2.1 ASan Configuration
ASan is mainly controlled by the ASAN_OPTIONS parameter, which is mainly used to set the detection level, output format, and the level of detail in memory error reports.
There are two main places for parameter configuration:
- In the app.json5 file of the project (with a higher priority)
- In Run/Debug Configurations
2.1.1 Configuration in app.json5
In the AppScope > app.json5 file:
{
"app": {
"appEnvironments": [
{
"name": "ASAN_OPTIONS",
"value": "log_exe_name=true abort_on_error=0 print_cmdline=true" // The example is for reference only, and the actual situation shall prevail.
},
],
...
}
}
2.1.2 Configuration in Run/Debug Configurations
Click the plus button to add a new configuration named ASAN_OPTIONS, and the value can be set to: log_exe_name=true abort_on_error=0 print_cmdline=true
.
2.1.3 Description of Configurable Parameters
Parameter | Default Value | Required | Meaning |
---|---|---|---|
log_exe_name | true | Yes | It cannot be modified. It specifies whether the name of the executable file should be included in the memory error log. |
log_path | /dev/asanlog/asan.log | No | It is required when the ROM version is less than NEXT.0.0.68, and the value cannot be modified. It is no longer needed for versions NEXT.0.0.68 and above. |
abort_on_error | false | Yes | Specifies whether to call abort() or _exit() after printing the error report. - false: Use _exit() to end the process after printing the error report. - true: Use abort() to end the process after printing the error report. |
strip_path_prefix | - | No | Remove the configured prefix from the file path in the memory error log. For example: /data/storage/el1 |
detect_stack_use_after_return | false | No | Specifies whether to check access to the stack space that has already been released. - true: Check. - false: Do not check. |
halt_on_error | 0 | No | Specifies whether to continue running after detecting a memory error. - 0 means continue running. - 1 means stop running. |
malloc_context_size | - | No | The number of call stack layers displayed when a memory error occurs. |
suppressions | "" | No | File names to be masked. |
handle_segv | - | No | Check for segmentation faults. |
handle_sigill | - | No | Check for SIGILL signals. |
quarantine_size_mb | 256 | No | Specifies the size of the quarantine area for detecting errors in accessing the stack space that has already been released. |
2.2 Enabling and Starting ASan
There are two ways to use ASan:
- In the run/debug window, click Diagnostics and check Address Sanitizer.
- In AppScope/app.json5, add the ASan configuration switch.
It is recommended to make changes in the IDE configuration whenever possible instead of modifying the code.
If there is a dependent local library, you need to configure the arguments field in the build-profile.json5 file of the library module with the value "-DOHOS_ENABLE_ASAN=ON", which means compiling the.so file in ASan mode.
Next, when running or debugging the current application, when a memory error occurs in the program, the ASan log information will pop up. Clicking on the link in the information will allow you to jump to the code location that caused the memory error.
The following is the asan log after simulating an array out-of-bounds exception crash. Clicking on the error will directly jump to the location that caused the crash, and it will also prompt UNKNOWN memory access. Such logs are very helpful for us to locate problems.
2.3 ASan Detection Error Codes
ASan will prompt specific error codes. The following lists the plugin error codes and their corresponding reasons.
2.3.1 heap-buffer-overflow
- Cause and Impact: Out-of-bounds access, which leads to security vulnerabilities in the program and a risk of crashing.
- Optimization Suggestions: For collections with known sizes, pay attention to not accessing out of bounds. For collections with variable sizes, check the size before accessing.
- Error Code Example:
int heapBufferOverflow() {
char *buffer;
buffer = (char *)malloc(10);
*(buffer + 11) = 'n';
*(buffer + 12) = 'n';
free(buffer);
return buffer[1];
}
2.3.2 stack-buffer-underflow
- Cause and Impact: Accessing below the lower bound, which leads to security vulnerabilities in the program and a risk of crashing.
- Optimization Suggestions: The access index should not be less than the lower bound.
- Error Code Example:
int stackBufferUnderflow() {
int subscript = -1;
char buffer[42];
buffer[subscript] = 42;
return 0;
}
2.3.3 stack-use-after-scope
- Cause and Impact: Stack variables are used outside their scope, which leads to security vulnerabilities in the program and a risk of crashing.
- Optimization Suggestions: Pay attention to the scope of variables.
- Error Code Example:
int *gp;
bool b = true;
int stackUseAfterScope() {
if (b) {
int x[5];
gp = x + 1;
}
return *gp;
}
2.3.4 attempt-free-nonallocated-memory
- Cause and Impact: Attempting to release non-heap objects or unallocated memory, which leads to security vulnerabilities in the program and a risk of crashing.
- Optimization Suggestions: Do not use the free function on non-heap objects or unallocated memory.
- Error Code Example:
int main() {
int value = 42;
free(&value);
return 0;
}
2.3.5 double-free
- Cause and Impact: Releasing memory multiple times, which leads to security vulnerabilities in the program and a risk of crashing.
- Optimization Suggestions: Initialize variables to NULL when they are defined and declared. Immediately reset the variables to NULL after releasing memory. In this way, you can check whether the variables are NULL before each release to determine whether they can be released.
- Error Code Example:
int main() {
int *x = new int[42];
delete [] x;
delete [] x;
return 0;
}
2.3.6 heap-use-after-free
- Cause and Impact: When the memory pointed to by a pointer has been released and the program still accesses the released memory through that pointer, heap-use-after-free will be triggered, which leads to security vulnerabilities in the program and a risk of crashing.
- Optimization Suggestions: Implement an alternative version of free() or a delete destructor to ensure pointer resetting.
- Error Code Example:
#include <stdlib.h>
int main() {
int *array = new int[5];
delete[] array;
return array[5];
}
2.4 Other Precautions
If any module within an application enables ASan, the entry module must also enable ASan. If the entry module does not enable ASan, the application will crash at startup and a CPP Crash error will be reported.
3. TSan Detection
Another issue in C++ development is threading problems. Introducing multi-threading can make the execution order of code unpredictable, making it difficult to debug and locate problems. Fortunately, DevEco Studio provides the TSan tool. TSan, short for ThreadSanitizer, is a tool for detecting data races. It consists of a compiler instrumentation module and a runtime library.
3.1 Main Application Scenarios
Common problems in multi-threaded development include thread safety, deadlocks, resource contention, and thread pool management issues. These problems can all be checked with the help of TSan. TSan can detect the following problems:
- Data Race Detection: A data race occurs when two or more threads access the same memory location simultaneously without proper synchronization mechanisms, and at least one of the threads is writing to it. Data races are one of the main reasons for unpredictable behavior in multi-threaded programs.
-
Lock Error Detection: TSan can not only detect data races but also detect errors related to locks:
- Deadlock: A deadlock occurs when two or more threads are waiting for each other to release locks, causing the program to be unable to continue execution.
- Double Unlock: The same thread attempts to unlock a lock that has already been unlocked.
- Unlock Without Holding the Lock: A thread attempts to unlock a lock that it does not hold.
-
Condition Variable Error Detection: Condition variables are used for communication and synchronization between threads. Common errors include:
- Wait Without Holding the Lock: A thread calls wait without holding the relevant lock.
- Signal or Broadcast Without Holding the Lock: A thread calls signal or broadcast without holding the relevant lock.
3.2 TSan Configuration
There are two ways to configure and enable TSan:
- In the run/debug window, click Diagnostics and check Thread Sanitizer.
- Modify the AppScope/app.json5 file in the project directory and add the TSan configuration switch.
Note: If there is a reference to a local library, you need to configure the arguments field in the build-profile.json5 file of the library module with the value "-DOHOS_ENABLE_TSAN=ON", which means compiling the.so file in TSan mode.
3.3 Starting TSan
When running or debugging the current application, when a thread error occurs in the program, the TSan log information will pop up. Clicking on the link in the information will allow you to jump to the code location that caused the thread error.
When TSan detects an error, it will generate a detailed report, including:
- Error type: Such as data race, deadlock, etc.
- Memory address: The memory addresses involved.
- Thread information: The thread IDs involved and the stack trace of thread creation.
- Source code location: The source code location of each memory access and the stack trace.
- Context information: Access type (read/write), access size, etc.
Note:
Currently, there will be false positives in TSan when using the call_once interface. Developers can add attribute((no_sanitize("thread"))) before the function that calls this interface to shield this problem.
3.4 Other Precautions
- After enabling TSan, the performance will be reduced by 5 to 15 times, and the memory occupancy rate will increase by 5 to 10 times. Other functions that apply for large virtual memory (such as GPU graphics rendering) may be affected.
- ASan and TSan cannot be enabled simultaneously.
- TSan only supports API version 12 and above.
4. Summary
This article mainly introduced two memory debugging and thread debugging tools provided by DevEco Studio: ASan (Address-Sanitizer) and TSan (Thread-Sanitizer). These two tools can help developers detect and solve memory errors and thread problems in C++ programs. ASan is used to detect out-of-bounds addresses, unreleased memory, and memory that has been released and then released again, while TSan is used to detect data races, lock errors, and condition variable errors.
DevEco Studio is quite powerful and user-friendly for developers. It helps solve and locate the problems encountered in C++ development through tools. With the help of memory and thread detection tools, developers can handle C++ development tasks with ease. It is recommended that everyone become familiar with these two tools so that they can be confident when facing complex C++ development scenarios and use the tools to eliminate potential risks.
Top comments (0)