Structural testing or white-box testing can effectively detect logic, control flow, calculations, and data errors in your code. This test requires an in-depth view of the internal workings of the software (hence the name "white box" or "glass box") to understand the details of the software structure. It checks each conditional expression, math operation, input, and output. Because of the many details that need to be tested, the structural test checks one software unit at a time, usually a function or class.
Code reviews also use techniques that are as complex as implementing defects and potential problem finding. As with white-box testing, reviews are usually conducted for each unit of the software, as an effective review process requires a centralized and exhaustive inspection.
Unlike censorship and white-box testing, functional tests or black-box tests assume no knowledge of the implementation of the software, which tests the output driven by the controlled input. Functional tests consist of test procedures written by testers or developers that specify the expected program output for a particular set of program inputs. After the test runs, the tester compares the actual output to the expected output to find the problem. Black box testing can effectively identify unfulfilled requirements, interface issues, performance issues, and errors in the most common features of the program.
While combining these techniques can find most of the errors hidden in a particular software program, they also have limitations. Code review and white-box testing only target a small portion of the code at a time, ignoring the rest of the system. Black box testing usually treats the system as a whole, ignoring the details of the implementation. Some important issues can only be discovered by focusing on the details of their interactions throughout the system; traditional methods cannot reliably identify these problems. The software system must be checked in its entirety to find the specific cause of the specific problem. Since it is generally not possible to thoroughly and thoroughly analyze the details of the program and its interaction with all other parts of the code, the analysis should address specific aspects of the program that are known to cause problems.
This article will explore three potential problem areas:
* Stack overflow
* Competition conditions
Readers can read the second part of this article online, which will explore the following questions:
* Timing issues
* Reentrant conditions
All of the above problems are quite common in systems that employ multitasking real-time design techniques.
The processor uses the stack to store temporary variables, pass parameters to the called function, save the thread "state", and so on. If the system does not use virtual memory (in other words, it cannot transfer memory pages to disk to free up memory for other purposes), the stack will be fixed to the factory size. If for some reason the stack goes beyond the range of numbers assigned by the programmer, the program becomes undefined. This instability can cause serious system failures. Therefore, it is important to ensure that the system is able to allocate enough stacks in the worst case.
The only way to ensure that a stack overflow never occurs is to analyze the code, determine the maximum stack usage for the program under all possible conditions, and then check if enough stacks are allocated. Testing is unlikely to trigger a specific combination of transient inputs leading to a worst-case scenario.
The concept of stack depth analysis is relatively simple:
1. Create a call tree for each individual thread.
2. Determine the stack usage for each function in the call tree.
3. Examine each call tree to determine which call path from the root of the tree to the outer "leaf" needs to use the most stack.
4. Add the maximum stack usage for each individual thread call tree.
5. Determine the maximum stack usage for each Interrupt Service Routine (ISR) within each interrupt priority level and calculate the sum. However, if the ISR itself does not have a stack and uses the stack of interrupted threads, then the maximum number of stacks used by the ISR should be added to each thread stack.
6. For each priority, plus the number of stacks used to hold the processor state when the interrupt occurs.
7. If using an RTOS, add the maximum number of stacks required for the RTOS's own internal use (unlike the system call raised by the application code, which is included in step 2).
In addition to this, there are two important things to consider. First, the call tree built only from high-level language source code is probably not perfect. Most compilers use a run-time library to optimize common computational tasks, such as multiplication and division of large integers, floating-point operations, etc. These calls are only visible in the assembly language produced by the compiler. The runtime library functions themselves may use a large amount of stack space, which must be included in the analysis. If you are using the C++ language, all of the following types of functions (methods) must also be included in the call tree: constructors, destructors, overloaded operators, copy constructors, and conversion functions. All function pointers must also be parsed and the functions they call are included in the analysis.