Skip Navigation Links | |
Exit Print View | |
Oracle Solaris Studio 12.3: Thread Analyzer User's Guide Oracle Solaris Studio 12.3 Information Library |
1. What is the Thread Analyzer and What Does It Do?
2.1 Data Race Tutorial Source Files
2.1.1 Getting the Data Race Tutorial Source Files
2.1.2 Source Code for prime_omp.c
2.1.3 Source Code for prime_pthr.c
2.1.3.1 Effect of Data Races in prime_omp.c and prime_pthr.c
2.2 How to Use the Thread Analyzer to Find Data Races
2.2.1.1 To Instrument Source Code
2.2.1.2 To Instrument Binary Code
2.2.2 Create a Data Race Detection Experiment
2.2.3 Examine the Data Race Detection Experiment
2.2.3.1 Using Thread Analyzer to View the Data Race Experiment
2.2.3.2 Using er_print to View the Data Race Experiment
2.3 Understanding the Experiment Results
2.3.1 Data Races in prime_omp.c
2.3.2 Data Races in prime_pthr.c
2.3.3 Call Stack Traces of Data Races
2.4 Diagnosing the Cause of a Data Race
2.4.1 Check Whether or Not the Data Race is a False Positive
2.4.2 Check Whether or Not the Data Race is Benign
2.4.3 Fix the Bug, Not the Data Race
2.4.3.1 Fixing Bugs in prime_omp.c
2.4.3.2 Fixing Bugs in prime_pthr.c
2.5.1 User-Defined Synchronizations
2.5.2 Memory That is Recycled by Different Threads
2.6.1 A Program for Finding Primes
2.6.2 A Program that Verifies Array-Value Types
2.6.3 A Program Using Double-Checked Locking
This tutorial relies on two programs, both of which contain data races:
The first program finds prime numbers. It is written with C and is parallelized with OpenMP directives. The source file is called prime_omp.c.
The second program also finds prime numbers and is also written with C. However, it is parallelized with POSIX threads instead of OpenMP directives. The source file is called prime_pthr.c.
You can download the source files used in this tutorial from the samples download area of the Oracle Solaris Studio developer portal.
After you download and unpack the sample files, you can find the samples in the SolarisStudioSampleApplications/ThreadAnalyzer directory. The samples are located in the prime_omp and prime_pthr subdirectories. Each sample directory includes a Makefile and a DEMO file of instructions, but this tutorial does not follow those instructions or use the Makefile. Instead, you are instructed to execute commands individually.
To follow the tutorial, you can copy the prime_omp.c and prime_pthr.c files from the samples directories to a different directory, or you can create your own files and copy the code from the following code listings.
The source code for prime_omp.c is shown below:
1 /* 2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved. 3 * @(#)prime_omp.c 1.3 (Oracle) 10/03/26 4 */ 5 6 #include <stdio.h> 7 #include <math.h> 8 #include <omp.h> 9 10 #define THREADS 4 11 #define N 10000 12 13 int primes[N]; 14 int pflag[N]; 15 16 int is_prime(int v) 17 { 18 int i; 19 int bound = floor(sqrt(v)) + 1; 20 21 for (i = 2; i < bound; i++) { 22 /* no need to check against known composites */ 23 if (!pflag[i]) 24 continue; 25 if (v % i == 0) { 26 pflag[v] = 0; 27 return 0; 28 } 29 } 30 return (v > 1); 31 } 32 33 int main(int argn, char **argv) 34 { 35 int i; 36 int total = 0; 37 38 #ifdef _OPENMP 39 omp_set_dynamic(0); 40 omp_set_num_threads(THREADS); 41 #endif 42 43 for (i = 0; i < N; i++) { 44 pflag[i] = 1; 45 } 46 47 #pragma omp parallel for 48 for (i = 2; i < N; i++) { 49 if ( is_prime(i) ) { 50 primes[total] = i; 51 total++; 52 } 53 } 54 55 printf("Number of prime numbers between 2 and %d: %d\n", 56 N, total); 57 58 return 0; 59 }
The source code for prime_pthr.c is shown below:
1 /* 2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved. 3 * @(#)prime_pthr.c 1.4 (Oracle) 10/03/26 4 */ 5 6 #include <stdio.h> 7 #include <math.h> 8 #include <pthread.h> 9 10 #define THREADS 4 11 #define N 10000 12 13 int primes[N]; 14 int pflag[N]; 15 int total = 0; 16 17 int is_prime(int v) 18 { 19 int i; 20 int bound = floor(sqrt(v)) + 1; 21 22 for (i = 2; i < bound; i++) { 23 /* no need to check against known composites */ 24 if (!pflag[i]) 25 continue; 26 if (v % i == 0) { 27 pflag[v] = 0; 28 return 0; 29 } 30 } 31 return (v > 1); 32 } 33 34 void *work(void *arg) 35 { 36 int start; 37 int end; 38 int i; 39 40 start = (N/THREADS) * (*(int *)arg); 41 end = start + N/THREADS; 42 for (i = start; i < end; i++) { 43 if ( is_prime(i) ) { 44 primes[total] = i; 45 total++; 46 } 47 } 48 return NULL; 49 } 50 51 int main(int argn, char **argv) 52 { 53 int i; 54 pthread_t tids[THREADS-1]; 55 56 for (i = 0; i < N; i++) { 57 pflag[i] = 1; 58 } 59 60 for (i = 0; i < THREADS-1; i++) { 61 pthread_create(&tids[i], NULL, work, (void *)&i); 62 } 63 64 i = THREADS-1; 65 work((void *)&i); 66 67 for (i = 0; i < THREADS-1; i++) { 68 pthread_join(tids[i], NULL); 69 } 70 71 printf("Number of prime numbers between 2 and %d: %d\n", 72 N, total); 73 74 return 0; 75 }
The order of memory accesses is non-deterministic when code contains a race condition and the computation gives different results from run to run. The correct answer in the prime_omp and prime_pthr programs is 1229.
You can compile and run the examples so you can see that each execution of prime_omp or prime_pthr produces incorrect and inconsistent results because of the data races in the code.
In the following example, type the commands in bold to compile and run the prime_omp program:
% cc -xopenmp=noopt -o prime_omp prime_omp.c -lm % % ./prime_omp Number of prime numbers between 2 and 10000: 1229 % ./prime_omp Number of prime numbers between 2 and 10000: 1228 % ./prime_omp Number of prime numbers between 2 and 10000: 1180
In the following example, type the commands in bold to compile and run the prime_pthr program:
% cc -mt -o prime_pthr prime_pthr.c -lm % % ./prime_pthr Number of prime numbers between 2 and 10000: 1140 % ./prime_pthr Number of prime numbers between 2 and 10000: 1122 % ./prime_pthr Number of prime numbers between 2 and 10000: 1141
Notice the inconsistency of the results of the three runs of each program. You may need to run the programs more than three times to see inconsistent results.
Next you instrument the code and create experiments so you can find where the data races are occurring.