DEV Community

Cover image for Introduction to DevEco Studio, a Development Tool for HarmonyOS Next: ASan and TSan Detection to Cure Your Fear of C++
kouwei qing
kouwei qing

Posted on

Introduction to DevEco Studio, a Development Tool for HarmonyOS Next: ASan and TSan Detection to Cure Your Fear of C++

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.
      },
    ],
   ...
  }
}
Enter fullscreen mode Exit fullscreen mode

2.1.2 Configuration in Run/Debug Configurations

Image description

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:

  1. In the run/debug window, click Diagnostics and check Address Sanitizer.

Image description

  1. In AppScope/app.json5, add the ASan configuration switch.

Image description

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.

Image description

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.

Image description

Image description

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];
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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]; 
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. In the run/debug window, click Diagnostics and check Thread Sanitizer.

Image description

  1. Modify the AppScope/app.json5 file in the project directory and add the TSan configuration switch.

Image description

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.

Image description

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)