]> git.saurik.com Git - apple/security.git/blob - utilities/Regressions/su-41-secdb-stress.c
Security-55471.14.tar.gz
[apple/security.git] / utilities / Regressions / su-41-secdb-stress.c
1 //
2 // su-41-secdb-stress.c
3 // utilities
4 //
5 // Created by Michael Brouwer on 7/25/13.
6 // Copyright (c) 2013 Apple Inc. All rights reserved.
7 //
8
9 #include <utilities/SecCFWrappers.h>
10 #include <utilities/SecDb.h>
11 #include <utilities/SecDispatchRelease.h>
12
13 #include <CoreFoundation/CoreFoundation.h>
14
15 #include "utilities_regressions.h"
16 #include <time.h>
17
18 #define kTestCount 3415
19
20 // Queue to protect counters and test_ok invocations
21 static dispatch_queue_t count_queue;
22
23 #define ts_ok(THIS, ...) \
24 ({ \
25 bool is_ok = !!(THIS); \
26 dispatch_sync(count_queue, ^{ \
27 test_ok(is_ok, test_create_description(__VA_ARGS__), test_directive, \
28 test_reason, __FILE__, __LINE__, NULL); \
29 }); \
30 is_ok; \
31 })
32
33 #define ts_ok_status(THIS, ...) \
34 ({ \
35 OSStatus _this = (THIS); \
36 __block bool is_ok; \
37 dispatch_sync(count_queue, ^{ \
38 is_ok = test_ok(!_this, test_create_description(__VA_ARGS__), \
39 test_directive, test_reason, __FILE__, __LINE__, \
40 "# status: %s(%ld)\n", \
41 sec_errstr(_this), _this); \
42 }); \
43 is_ok; \
44 })
45
46
47 typedef void (^SecDbBlock)(SecDbConnectionRef dbconn);
48
49 #define SecDbExecWithSql(dbconn, sql) test_SecDbExecWithSql(dbconn, sql, test_directive, test_reason, __FILE__, __LINE__)
50
51 static void test_SecDbExecWithSql(SecDbConnectionRef dbconn, CFStringRef sql CF_CONSUMED, const char *directive,
52 const char *reason, const char *file, unsigned line) {
53 CFErrorRef execError = NULL;
54 bool is_ok = !!(SecDbExec(dbconn, sql, &execError));
55 dispatch_sync(count_queue, ^{
56 test_ok(is_ok, test_create_description("exec %@: %@", sql, execError), directive, reason, file, line, NULL);
57 });
58 CFReleaseNull(execError);
59 CFReleaseSafe(sql);
60 }
61
62 #define SecDbDeleteWithInts(dbconn, key, value) test_SecDbDeleteWithInts(dbconn, key, value, test_directive, test_reason, __FILE__, __LINE__)
63 static void test_SecDbDeleteWithInts(SecDbConnectionRef dbconn, int key, int value, const char *directive,
64 const char *reason, const char *file, unsigned line) {
65 test_SecDbExecWithSql(dbconn, CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
66 CFSTR("DELETE FROM tablea WHERE key=%d AND value=%d;"), key, value), directive, reason, file, line);
67 }
68
69 static void SecDbDoReadOp(SecDbConnectionRef dbconn, size_t seed) {
70 switch (seed % 2) {
71 case 0:
72 {
73 CFErrorRef prepareError = NULL;
74 CFStringRef sql = CFSTR("SELECT key,value FROM tablea;");
75 ts_ok(SecDbPrepare(dbconn, sql, &prepareError, ^void (sqlite3_stmt *stmt) {
76 CFErrorRef stepError = NULL;
77 ts_ok(SecDbStep(dbconn, stmt, &stepError, ^(bool *stop) {
78 //const unsigned char *key = sqlite3_column_text(stmt, 1);
79 //pass("got a row key: %s", key);
80 // A row happened, we're done
81 *stop = true;
82 }), "SecDbStep: %@", stepError);
83 CFReleaseNull(stepError);
84 }), "SecDbPrepare: %@", prepareError);
85 CFReleaseNull(prepareError);
86 break;
87 }
88 case 1:
89 {
90 CFErrorRef prepareError = NULL;
91 CFStringRef sql = CFSTR("SELECT key,value FROM tablea;");
92 ts_ok(SecDbPrepare(dbconn, sql, &prepareError, ^void (sqlite3_stmt *stmt) {
93 CFErrorRef stepError = NULL;
94 ts_ok(SecDbStep(dbconn, stmt, &stepError, ^(bool *stop) {
95 //const unsigned char *key = sqlite3_column_text(stmt, 1);
96 //pass("got a row key: %s", key);
97 }), "SecDbStep: %@", stepError);
98 CFReleaseNull(stepError);
99 sqlite3_reset(stmt);
100 ts_ok(SecDbStep(dbconn, stmt, &stepError, ^(bool *stop) {
101 //const unsigned char *key = sqlite3_column_text(stmt, 1);
102 //pass("got a row key: %s", key);
103 *stop = true;
104 }), "SecDbStep: %@", stepError);
105 CFReleaseNull(stepError);
106 }), "SecDbPrepare: %@", prepareError);
107 CFReleaseNull(prepareError);
108 break;
109 }
110 }
111 }
112
113 static void SecDbDoWriteOp(SecDbConnectionRef dbconn, size_t seed) {
114 switch (seed % 6) {
115 case 0:
116 SecDbExecWithSql(dbconn, CFSTR("INSERT INTO tablea(key,value)VALUES(1,2);"));
117 break;
118 case 1:
119 {
120 CFErrorRef txnError = NULL;
121 ts_ok(SecDbTransaction(dbconn, kSecDbExclusiveTransactionType, &txnError, ^(bool *commit) {
122 CFErrorRef execError = NULL;
123 ts_ok(SecDbExec(dbconn, CFSTR("INSERT INTO tablea (key,value)VALUES(13,21);"), &execError),
124 "exec: %@", execError);
125 CFReleaseNull(execError);
126 ts_ok(SecDbExec(dbconn, CFSTR("INSERT INTO tablea (key,value)VALUES(2,5);"), &execError),
127 "exec: %@", execError);
128 CFReleaseNull(execError);
129 }), "SecDbTransaction: %@", txnError);
130 CFReleaseNull(txnError);
131 break;
132 }
133 case 2:
134 {
135 CFErrorRef prepareError = NULL;
136 CFStringRef sql = CFSTR("INSERT INTO tablea(key,value)VALUES(?,?);");
137 ts_ok(SecDbPrepare(dbconn, sql, &prepareError, ^void (sqlite3_stmt *stmt) {
138 CFErrorRef stepError = NULL;
139 ts_ok_status(sqlite3_bind_text(stmt, 1, "key1", 4, NULL), "bind_text[1]");
140 ts_ok_status(sqlite3_bind_blob(stmt, 2, "value1", 6, NULL), "bind_blob[2]");
141 ts_ok(SecDbStep(dbconn, stmt, &stepError, NULL), "SecDbStep: %@", stepError);
142 CFReleaseNull(stepError);
143 }), "SecDbPrepare: %@", prepareError);
144 CFReleaseNull(prepareError);
145 break;
146 }
147 case 3:
148 SecDbDeleteWithInts(dbconn, 1, 2);
149 break;
150 case 4:
151 SecDbDeleteWithInts(dbconn, 13, 21);
152 break;
153 case 5:
154 SecDbDeleteWithInts(dbconn, 2, 5);
155 break;
156 }
157 }
158
159 static void tests(void)
160 {
161 count_queue = dispatch_queue_create("count_queue", DISPATCH_QUEUE_SERIAL);
162
163 CFTypeID typeID = SecDbGetTypeID();
164 CFStringRef tid = CFCopyTypeIDDescription(typeID);
165 ts_ok(CFEqual(CFSTR("SecDb"), tid), "TypeIdDescription is SecDb");
166 CFReleaseNull(tid);
167
168 typeID = SecDbConnectionGetTypeID();
169 tid = CFCopyTypeIDDescription(typeID);
170 ts_ok(CFEqual(CFSTR("SecDbConnection"), tid), "TypeIdDescription is SecDbConnection");
171 CFReleaseNull(tid);
172
173 const char *home_var = getenv("HOME");
174 CFStringRef dbName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/Library/Keychains/su-41-sqldb-stress.db"), home_var ? home_var : "");
175 CFStringPerformWithCString(dbName, ^(const char *path) { unlink(path); });
176
177 SecDbRef db = SecDbCreate(dbName, ^bool (SecDbConnectionRef dbconn, bool did_create, CFErrorRef *firstOpenError) {
178 // This test will run when the database is first opened.
179 return ts_ok(SecDbExec(dbconn, CFSTR("CREATE TABLE tablea(key TEXT,value BLOB);"), firstOpenError),
180 "create table: %@", *firstOpenError);
181 });
182 ts_ok(db, "SecDbCreate");
183
184 __block CFIndex max_idle = 0;
185 __block CFIndex max_readers = 0;
186 __block CFIndex max_writers = 0;
187 __block CFIndex cur_readers = 0;
188 __block CFIndex cur_writers = 0;
189
190 dispatch_group_t group = dispatch_group_create();
191 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
192 for (size_t job=0; job < 1000; ++job) {
193 dispatch_group_async(group, queue, ^{
194 CFIndex cur_idle = SecDbIdleConnectionCount(db);
195 dispatch_sync(count_queue, ^{ if (max_idle < cur_idle) max_idle = cur_idle; });
196 CFErrorRef performError = NULL;
197 if (job % 7 == 0) {
198 ts_ok(SecDbPerformWrite(db, &performError, ^void (SecDbConnectionRef dbconn) {
199 dispatch_sync(count_queue, ^{ cur_writers++; if (max_writers < cur_writers) max_writers = cur_writers; });
200 SecDbDoWriteOp(dbconn, job);
201 dispatch_sync(count_queue, ^{ cur_writers--; });
202 }), "write %@", performError);
203 } else {
204 CFErrorRef performError = NULL;
205 ts_ok(SecDbPerformRead(db, &performError, ^void (SecDbConnectionRef dbconn) {
206 dispatch_sync(count_queue, ^{ cur_readers++; if (max_readers < cur_readers) max_readers = cur_readers; });
207 SecDbDoReadOp(dbconn, job);
208 dispatch_sync(count_queue, ^{ cur_readers--; });
209 }), "read %@", performError);
210 }
211 CFReleaseNull(performError);
212 });
213 }
214 dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
215 dispatch_release(group);
216
217 CFErrorRef writeError = NULL;
218 ts_ok(SecDbPerformWrite(db, &writeError, ^(SecDbConnectionRef dbconn){
219 SecDbExecWithSql(dbconn, CFSTR("DROP TABLE tablea;"));
220 }), "SecDbPerformWrite: %@", writeError);
221 CFReleaseNull(writeError);
222
223 dispatch_release_null(count_queue);
224
225 is(max_idle, kSecDbMaxIdleHandles, "max idle connection count is %d", kSecDbMaxIdleHandles);
226 is(max_writers, kSecDbMaxWriters, "max writers is %d", kSecDbMaxWriters);
227 is(max_readers, kSecDbMaxReaders, "max readers is %d", kSecDbMaxReaders);
228
229 CFReleaseNull(db);
230 }
231
232 int su_41_secdb_stress(int argc, char *const *argv)
233 {
234 plan_tests(kTestCount);
235 tests();
236
237 return 0;
238 }