1// Test whether mmap'ing profile counters onto an open file is feasible. As
2// this involves some platform-specific logic, this test is designed to be a
3// minimum viable proof-of-concept: it may be useful when porting the mmap()
4// mode to a new platform, but is not in and of itself a test of the profiling
5// runtime.
6
7// REQUIRES: darwin
8
9// Align counters and data to the maximum expected page size (16K).
10// RUN: %clang -g -o %t %s \
11// RUN: -Wl,-sectalign,__DATA,__pcnts,0x4000 \
12// RUN: -Wl,-sectalign,__DATA,__pdata,0x4000
13
14// Create a 'profile' using mmap() and validate it.
15// RUN: %run %t create %t.tmpfile
16// RUN: %run %t validate %t.tmpfile
17
18#include <fcntl.h>
19#include <unistd.h>
20#include <sys/mman.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24
25__attribute__((section("__DATA,__pcnts"))) int counters[] = {0xbad};
26extern int cnts_start __asm("section$start$__DATA$__pcnts");
27const size_t cnts_len = 0x4000;
28
29__attribute__((section("__DATA,__pdata"))) int data[] = {1, 2, 3};
30extern int data_start __asm("section$start$__DATA$__pdata");
31const size_t data_len = sizeof(int) * 3;
32
33int create_tmpfile(char *path) {
34 // Create a temp file.
35 int fd = open(file: path, O_RDWR | O_TRUNC | O_CREAT, 0666);
36 if (fd == -1) {
37 perror(s: "open");
38 return EXIT_FAILURE;
39 }
40
41 // Grow the file to hold data and counters.
42 if (0 != ftruncate(fd: fd, length: cnts_len + data_len)) {
43 perror(s: "ftruncate");
44 return EXIT_FAILURE;
45 }
46
47 // Write the data first (at offset 0x4000, after the counters).
48 if (data_len != pwrite(fd: fd, buf: &data, n: data_len, offset: 0x4000)) {
49 perror(s: "write");
50 return EXIT_FAILURE;
51 }
52
53 // Map the counters into the file, before the data.
54 //
55 // Requirements (on Darwin):
56 // - &cnts_start must be page-aligned.
57 // - The length and offset-into-fd must be page-aligned.
58 int *counter_map = (int *)mmap(addr: &cnts_start, len: 0x4000, PROT_READ | PROT_WRITE,
59 MAP_FIXED | MAP_SHARED, fd: fd, offset: 0);
60 if (counter_map != &cnts_start) {
61 perror(s: "mmap");
62 return EXIT_FAILURE;
63 }
64
65 // Update counters 1..9. These updates should be visible in the file.
66 // Expect counter 0 (0xbad), which is not updated, to be zero in the file.
67 for (int i = 1; i < 10; ++i)
68 counter_map[i] = i;
69
70 // Intentionally do not msync(), munmap(), or close().
71 return EXIT_SUCCESS;
72}
73
74int validate_tmpfile(char *path) {
75 int fd = open(file: path, O_RDONLY);
76 if (fd == -1) {
77 perror(s: "open");
78 return EXIT_FAILURE;
79 }
80
81 // Verify that the file length is: sizeof(counters) + sizeof(data).
82 const size_t num_bytes = cnts_len + data_len;
83 int buf[num_bytes];
84 if (num_bytes != read(fd: fd, buf: &buf, nbytes: num_bytes)) {
85 perror(s: "read");
86 return EXIT_FAILURE;
87 }
88
89 // Verify the values of counters 1..9 (i.e. that the mmap() worked).
90 for (int i = 0; i < 10; ++i) {
91 if (buf[i] != i) {
92 fprintf(stderr,
93 format: "validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n",
94 i, i, buf[i]);
95 return EXIT_FAILURE;
96 }
97 }
98
99 // Verify that the rest of the counters (after counter 9) are 0.
100 const int num_cnts = 0x4000 / sizeof(int);
101 for (int i = 10; i < num_cnts; ++i) {
102 if (buf[i] != 0) {
103 fprintf(stderr,
104 format: "validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n",
105 0, i, buf[i]);
106 return EXIT_FAILURE;
107 }
108 }
109
110 // Verify that the data written after the counters is equal to the "data[]"
111 // array (i.e. {1, 2, 3}).
112 for (int i = num_cnts; i < num_cnts + 3; ++i) {
113 if (buf[i] != (i - num_cnts + 1)) {
114 fprintf(stderr,
115 format: "validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n",
116 i - num_cnts + 1, i, buf[i]);
117 return EXIT_FAILURE;
118 }
119 }
120
121 // Intentionally do not close().
122 return EXIT_SUCCESS;
123}
124
125int main(int argc, char **argv) {
126 intptr_t cnts_start_int = (intptr_t)&cnts_start;
127 intptr_t data_start_int = (intptr_t)&data_start;
128 int pagesz = getpagesize();
129
130 if (cnts_start_int % pagesz != 0) {
131 fprintf(stderr, format: "__pcnts is not page-aligned: 0x%lx.\n", cnts_start_int);
132 return EXIT_FAILURE;
133 }
134 if (data_start_int % pagesz != 0) {
135 fprintf(stderr, format: "__pdata is not page-aligned: 0x%lx.\n", data_start_int);
136 return EXIT_FAILURE;
137 }
138 if (cnts_start_int + 0x4000 != data_start_int) {
139 fprintf(stderr, format: "__pdata not ordered after __pcnts.\n");
140 return EXIT_FAILURE;
141 }
142
143 char *action = argv[1];
144 char *path = argv[2];
145 if (0 == strcmp(s1: action, s2: "create"))
146 return create_tmpfile(path);
147 else if (0 == strcmp(s1: action, s2: "validate"))
148 return validate_tmpfile(path);
149 else
150 return EXIT_FAILURE;
151}
152

source code of compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c