|
1 /************************************************************************************************ |
|
2 NC_ALLOC.CPP |
|
3 |
|
4 * Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
5 * |
|
6 * Copyright (c) 1997 |
|
7 * Mark of the Unicorn, Inc. |
|
8 * |
|
9 * Permission to use, copy, modify, distribute and sell this software |
|
10 * and its documentation for any purpose is hereby granted without fee, |
|
11 * provided that the above copyright notice appear in all copies and |
|
12 * that both that copyright notice and this permission notice appear |
|
13 * in supporting documentation. Mark of the Unicorn makes no |
|
14 * representations about the suitability of this software for any |
|
15 * purpose. It is provided "as is" without express or implied warranty. |
|
16 |
|
17 ************************************************************************************************/ |
|
18 |
|
19 #include "nc_alloc.h" |
|
20 #include <string> |
|
21 |
|
22 #if defined (EH_NEW_HEADERS) |
|
23 # include <new> |
|
24 # include <cassert> |
|
25 # include <cstdlib> |
|
26 #else |
|
27 # include <assert.h> |
|
28 # include <stdlib.h> |
|
29 # include <new.h> |
|
30 #endif |
|
31 |
|
32 #if defined (EH_NEW_IOSTREAMS) |
|
33 # include <iostream> |
|
34 #else |
|
35 # include <iostream.h> |
|
36 #endif |
|
37 |
|
38 long alloc_count = 0; |
|
39 long object_count = 0; |
|
40 long TestController::possible_failure_count = 0; |
|
41 const char* TestController::current_test = "<unknown>"; |
|
42 const char* TestController::current_test_category = "no category"; |
|
43 const char* TestController::current_container = 0; |
|
44 bool TestController::nc_verbose = true; |
|
45 bool TestController::never_fail = false; |
|
46 bool TestController::track_allocations = false; |
|
47 bool TestController::leak_detection_enabled = false; |
|
48 |
|
49 TestController gTestController; |
|
50 |
|
51 //************************************************************************************************ |
|
52 void TestController::maybe_fail(long) { |
|
53 if (never_fail || Failure_threshold() == kNotInExceptionTest) |
|
54 return; |
|
55 |
|
56 // throw if allocation would satisfy the threshold |
|
57 if (possible_failure_count++ >= Failure_threshold()) { |
|
58 // what about doing some standard new_handler() behavior here (to test it!) ??? |
|
59 |
|
60 // reset and simulate an out-of-memory failure |
|
61 Failure_threshold() = kNotInExceptionTest; |
|
62 #ifndef EH_NO_EXCEPTIONS |
|
63 throw EH_STD::bad_alloc(); |
|
64 #endif |
|
65 } |
|
66 } |
|
67 |
|
68 #if defined (EH_HASHED_CONTAINERS_IMPLEMENTED) |
|
69 # if defined (__SGI_STL) |
|
70 # if defined (EH_NEW_HEADERS) |
|
71 # include <hash_set> |
|
72 # else |
|
73 # include <hash_set.h> |
|
74 # endif |
|
75 # elif defined (__MSL__) |
|
76 # include <hashset.h> |
|
77 # else |
|
78 # error what do I include to get hash_set? |
|
79 # endif |
|
80 #else |
|
81 # if defined (EH_NEW_HEADERS) |
|
82 # include <set> |
|
83 # else |
|
84 # include <set.h> |
|
85 # endif |
|
86 #endif |
|
87 |
|
88 #if !defined (EH_HASHED_CONTAINERS_IMPLEMENTED) |
|
89 typedef EH_STD::set<void*, EH_STD::less<void*> > allocation_set; |
|
90 #else |
|
91 |
|
92 USING_CSTD_NAME(size_t) |
|
93 |
|
94 struct hash_void { |
|
95 size_t operator()(void* x) const { return (size_t)x; } |
|
96 }; |
|
97 |
|
98 typedef EH_STD::hash_set<void*, ::hash_void, EH_STD::equal_to<void*> > allocation_set; |
|
99 #endif |
|
100 |
|
101 static allocation_set& alloc_set() { |
|
102 static allocation_set s; |
|
103 return s; |
|
104 } |
|
105 |
|
106 // Prevents infinite recursion during allocation |
|
107 static bool using_alloc_set = false; |
|
108 |
|
109 #if !defined (NO_FAST_ALLOCATOR) |
|
110 // |
|
111 // FastAllocator -- speeds up construction of TestClass objects when |
|
112 // TESTCLASS_DEEP_DATA is enabled, and speeds up tracking of allocations |
|
113 // when the suite is run with the -t option. |
|
114 // |
|
115 class FastAllocator { |
|
116 public: |
|
117 //FastAllocator() : mFree(0), mUsed(0) {} |
|
118 static void *Allocate(size_t s) { |
|
119 void *result = 0; |
|
120 |
|
121 if (s <= sizeof(Block)) { |
|
122 if (mFree != 0) { |
|
123 result = mFree; |
|
124 mFree = mFree->next; |
|
125 } |
|
126 else if (mBlocks != 0 && mUsed < kBlockCount) { |
|
127 result = (void*)&mBlocks[mUsed++]; |
|
128 } |
|
129 } |
|
130 return result; |
|
131 } |
|
132 |
|
133 static bool Free(void* p) { |
|
134 Block* b = (Block*)p; |
|
135 if (mBlocks == 0 || b < mBlocks || b >= mBlocks + kBlockCount) |
|
136 return false; |
|
137 b->next = mFree; |
|
138 mFree = b; |
|
139 return true; |
|
140 } |
|
141 |
|
142 struct Block; |
|
143 friend struct Block; |
|
144 |
|
145 enum { |
|
146 // Number of fast allocation blocks to create. |
|
147 kBlockCount = 1500, |
|
148 |
|
149 // You may need to adjust this number for your platform. |
|
150 // A good choice will speed tests. A bad choice will still work. |
|
151 kMinBlockSize = 48 |
|
152 }; |
|
153 |
|
154 struct Block { |
|
155 union { |
|
156 Block *next; |
|
157 double dummy; // fbp - force alignment |
|
158 char dummy2[kMinBlockSize]; |
|
159 }; |
|
160 }; |
|
161 |
|
162 static Block* mBlocks; |
|
163 static Block *mFree; |
|
164 static size_t mUsed; |
|
165 }; |
|
166 |
|
167 FastAllocator::Block *FastAllocator::mBlocks = |
|
168 (FastAllocator::Block*)EH_CSTD::calloc( sizeof(FastAllocator::Block), FastAllocator::kBlockCount ); |
|
169 FastAllocator::Block *FastAllocator::mFree; |
|
170 size_t FastAllocator::mUsed; |
|
171 |
|
172 |
|
173 static FastAllocator gFastAllocator; |
|
174 #endif |
|
175 |
|
176 inline char* AllocateBlock(size_t s) { |
|
177 #if !defined (NO_FAST_ALLOCATOR) |
|
178 char * const p = (char*)gFastAllocator.Allocate( s ); |
|
179 if (p != 0) |
|
180 return p; |
|
181 #endif |
|
182 |
|
183 return (char*)EH_CSTD::malloc(s); |
|
184 } |
|
185 |
|
186 static void* OperatorNew( size_t s ) { |
|
187 if (!using_alloc_set) { |
|
188 simulate_possible_failure(); |
|
189 ++alloc_count; |
|
190 } |
|
191 |
|
192 char *p = AllocateBlock(s); |
|
193 |
|
194 if (gTestController.TrackingEnabled() && |
|
195 gTestController.LeakDetectionEnabled() && |
|
196 !using_alloc_set) { |
|
197 using_alloc_set = true; |
|
198 bool inserted = alloc_set().insert(p).second; |
|
199 EH_ASSERT(inserted); |
|
200 using_alloc_set = false; |
|
201 } |
|
202 |
|
203 return p; |
|
204 } |
|
205 #if 0 //sandeep |
|
206 void* _STLP_CALL operator new(size_t s) |
|
207 #ifdef EH_DELETE_HAS_THROW_SPEC |
|
208 throw(EH_STD::bad_alloc) |
|
209 #endif |
|
210 { return OperatorNew( s ); } |
|
211 |
|
212 #ifdef EH_USE_NOTHROW |
|
213 void* _STLP_CALL operator new(size_t size, const EH_STD::nothrow_t&) throw() { |
|
214 try { |
|
215 return OperatorNew( size ); |
|
216 } |
|
217 catch (...) { |
|
218 return 0; |
|
219 } |
|
220 } |
|
221 #endif |
|
222 |
|
223 #if defined (EH_VECTOR_OPERATOR_NEW) |
|
224 void* _STLP_CALL operator new[](size_t size ) throw(EH_STD::bad_alloc) { |
|
225 return OperatorNew( size ); |
|
226 } |
|
227 |
|
228 # ifdef EH_USE_NOTHROW |
|
229 void* _STLP_CALL operator new[](size_t size, const EH_STD::nothrow_t&) throw() { |
|
230 try { |
|
231 return OperatorNew(size); |
|
232 } |
|
233 catch (...) { |
|
234 return 0; |
|
235 } |
|
236 } |
|
237 # endif |
|
238 |
|
239 void _STLP_CALL operator delete[](void* ptr) throw() |
|
240 { operator delete( ptr ); } |
|
241 #endif |
|
242 |
|
243 #if defined (EH_DELETE_HAS_THROW_SPEC) |
|
244 void _STLP_CALL operator delete(void* s) throw() |
|
245 #else |
|
246 void _STLP_CALL operator delete(void* s) |
|
247 #endif |
|
248 { |
|
249 if ( s != 0 ) { |
|
250 if ( !using_alloc_set ) { |
|
251 --alloc_count; |
|
252 |
|
253 if ( gTestController.TrackingEnabled() && gTestController.LeakDetectionEnabled() ) { |
|
254 using_alloc_set = true; |
|
255 allocation_set::iterator p = alloc_set().find( (char*)s ); |
|
256 EH_ASSERT( p != alloc_set().end() ); |
|
257 alloc_set().erase( p ); |
|
258 using_alloc_set = false; |
|
259 } |
|
260 } |
|
261 # if ! defined (NO_FAST_ALLOCATOR) |
|
262 if ( !gFastAllocator.Free( s ) ) |
|
263 # endif |
|
264 EH_CSTD::free(s); |
|
265 } |
|
266 } |
|
267 #endif //sandeep |
|
268 |
|
269 |
|
270 /*=================================================================================== |
|
271 ClearAllocationSet (private helper) |
|
272 |
|
273 EFFECTS: Empty the set of allocated blocks. |
|
274 ====================================================================================*/ |
|
275 void TestController::ClearAllocationSet() { |
|
276 if (!using_alloc_set) { |
|
277 using_alloc_set = true; |
|
278 alloc_set().clear(); |
|
279 using_alloc_set = false; |
|
280 } |
|
281 } |
|
282 |
|
283 |
|
284 bool TestController::ReportLeaked() { |
|
285 EndLeakDetection(); |
|
286 |
|
287 if (using_alloc_set) |
|
288 EH_ASSERT( alloc_count == static_cast<int>(alloc_set().size()) ); |
|
289 |
|
290 if (alloc_count != 0 || object_count != 0) { |
|
291 EH_STD::cerr<<"\nEH TEST FAILURE !\n"; |
|
292 PrintTestName(true); |
|
293 if (alloc_count) |
|
294 EH_STD::cerr << "ERROR : " << alloc_count << " outstanding allocations.\n"; |
|
295 if (object_count) |
|
296 EH_STD::cerr << "ERROR : " << object_count << " non-destroyed objects.\n"; |
|
297 alloc_count = object_count = 0; |
|
298 return true; |
|
299 } |
|
300 return false; |
|
301 } |
|
302 |
|
303 |
|
304 |
|
305 /*=================================================================================== |
|
306 PrintTestName |
|
307 |
|
308 EFFECTS: Prints information about the current test. If err is false, ends with |
|
309 an ellipsis, because the test is ongoing. If err is true an error is being |
|
310 reported, and the output ends with an endl. |
|
311 ====================================================================================*/ |
|
312 |
|
313 void TestController::PrintTestName(bool err) { |
|
314 if (current_container) |
|
315 EH_STD::cerr<<"["<<current_container<<"] :"; |
|
316 EH_STD::cerr<<"testing "<<current_test <<" (" << current_test_category <<")"; |
|
317 if (err) |
|
318 EH_STD::cerr<<EH_STD::endl; |
|
319 else |
|
320 EH_STD::cerr<<" ... "; |
|
321 } |
|
322 |
|
323 void TestController::ReportSuccess(int count) { |
|
324 if (nc_verbose) |
|
325 EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl; |
|
326 } |
|
327 |
|
328 long& TestController::Failure_threshold() { |
|
329 static long failure_threshold = kNotInExceptionTest; |
|
330 return failure_threshold; |
|
331 } |
|
332 #if 0 // enable non inline functions |
|
333 void TestController::TrackAllocations(bool track) { |
|
334 track_allocations = track; |
|
335 } |
|
336 |
|
337 bool TestController::TrackingEnabled() { |
|
338 return track_allocations; |
|
339 } |
|
340 |
|
341 void TestController::SetFailureCountdown(long count) { |
|
342 Failure_threshold() = count; |
|
343 possible_failure_count = 0; |
|
344 } |
|
345 |
|
346 void TestController::CancelFailureCountdown() { |
|
347 Failure_threshold() = kNotInExceptionTest; |
|
348 } |
|
349 |
|
350 void TestController::BeginLeakDetection() { |
|
351 alloc_count = 0; |
|
352 object_count = 0; |
|
353 ClearAllocationSet(); |
|
354 leak_detection_enabled = true; |
|
355 } |
|
356 |
|
357 bool TestController::LeakDetectionEnabled() { |
|
358 return leak_detection_enabled; |
|
359 } |
|
360 |
|
361 void TestController::EndLeakDetection() { |
|
362 leak_detection_enabled = false; |
|
363 } |
|
364 |
|
365 void TestController::SetCurrentTestCategory(const char* str) { |
|
366 current_test_category = str; |
|
367 if (nc_verbose) |
|
368 PrintTestName(); |
|
369 } |
|
370 |
|
371 void TestController::SetCurrentContainer(const char* str) { |
|
372 current_container=str; |
|
373 } |
|
374 |
|
375 void TestController::SetCurrentTestName(const char* str) { |
|
376 current_test = str; |
|
377 } |
|
378 |
|
379 void TestController::SetVerbose(bool val) { |
|
380 nc_verbose = val; |
|
381 } |
|
382 |
|
383 void TestController::TurnOffExceptions() { |
|
384 never_fail = true; |
|
385 } |
|
386 #endif |