1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 2003-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
8 /*
9 * File hpmufn.c
10 *
11 */
12
13 #include "unicode/utypes.h"
14 #include "unicode/putil.h"
15 #include "unicode/uclean.h"
16 #include "unicode/uchar.h"
17 #include "unicode/ures.h"
18 #include "cintltst.h"
19 #include "unicode/utrace.h"
20 #include <stdlib.h>
21 #include <string.h>
22
23 /**
24 * This should align the memory properly on any machine.
25 */
26 typedef union {
27 long t1;
28 double t2;
29 void *t3;
30 } ctest_AlignedMemory;
31
32 static void TestHeapFunctions(void);
33
34 void addHeapMutexTest(TestNode **root);
35
36
37 void
addHeapMutexTest(TestNode ** root)38 addHeapMutexTest(TestNode** root)
39 {
40 addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" );
41 }
42
43 static int32_t gMutexFailures = 0;
44
45 #define TEST_STATUS(status, expected) UPRV_BLOCK_MACRO_BEGIN { \
46 if (status != expected) { \
47 log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \
48 __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailures++; \
49 } \
50 } UPRV_BLOCK_MACRO_END
51
52
53 #define TEST_ASSERT(expr) UPRV_BLOCK_MACRO_BEGIN { \
54 if (!(expr)) { \
55 log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__); \
56 gMutexFailures++; \
57 } \
58 } UPRV_BLOCK_MACRO_END
59
60
61 /* These tests do cleanup and reinitialize ICU in the course of their operation.
62 * The ICU data directory must be preserved across these operations.
63 * Here is a helper function to assist with that.
64 */
safeGetICUDataDirectory()65 static char *safeGetICUDataDirectory() {
66 const char *dataDir = u_getDataDirectory(); /* Returned string vanashes with u_cleanup */
67 char *retStr = NULL;
68 if (dataDir != NULL) {
69 retStr = (char *)malloc(strlen(dataDir)+1);
70 strcpy(retStr, dataDir);
71 }
72 return retStr;
73 }
74
75
76
77 /*
78 * Test Heap Functions.
79 * Implemented on top of the standard malloc heap.
80 * All blocks increased in size by 8 to 16 bytes, and the poiner returned to ICU is
81 * offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks"
82 * ends up being freed directly, without coming through us.
83 * Allocations are counted, to check that ICU actually does call back to us.
84 */
85 int gBlockCount = 0;
86 const void *gContext;
87
myMemAlloc(const void * context,size_t size)88 static void * U_CALLCONV myMemAlloc(const void *context, size_t size) {
89 (void)context; // suppress compiler warnings about unused variable
90 char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory));
91 if (retPtr != NULL) {
92 retPtr += sizeof(ctest_AlignedMemory);
93 }
94 gBlockCount ++;
95 return retPtr;
96 }
97
myMemFree(const void * context,void * mem)98 static void U_CALLCONV myMemFree(const void *context, void *mem) {
99 (void)context; // suppress compiler warnings about unused variable
100 char *freePtr = (char *)mem;
101 if (freePtr != NULL) {
102 freePtr -= sizeof(ctest_AlignedMemory);
103 }
104 free(freePtr);
105 }
106
107
108
myMemRealloc(const void * context,void * mem,size_t size)109 static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) {
110 (void)context; // suppress compiler warnings about unused variable
111 char *p = (char *)mem;
112 char *retPtr;
113
114 if (p!=NULL) {
115 p -= sizeof(ctest_AlignedMemory);
116 }
117 retPtr = realloc(p, size+sizeof(ctest_AlignedMemory));
118 if (retPtr != NULL) {
119 p += sizeof(ctest_AlignedMemory);
120 }
121 return retPtr;
122 }
123
124
TestHeapFunctions()125 static void TestHeapFunctions() {
126 UErrorCode status = U_ZERO_ERROR;
127 UResourceBundle *rb = NULL;
128 char *icuDataDir;
129 UVersionInfo unicodeVersion = {0,0,0,0};
130
131 icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back
132 * after doing u_cleanup(). */
133
134
135 /* Verify that ICU can be cleaned up and reinitialized successfully.
136 * Failure here usually means that some ICU service didn't clean up successfully,
137 * probably because some earlier test accidently left something open. */
138 ctest_resetICU();
139
140 /* Un-initialize ICU */
141 u_cleanup();
142
143 /* Can not set memory functions with NULL values */
144 status = U_ZERO_ERROR;
145 u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status);
146 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
147 status = U_ZERO_ERROR;
148 u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status);
149 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
150 status = U_ZERO_ERROR;
151 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status);
152 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
153
154 /* u_setMemoryFunctions() should work with null or non-null context pointer */
155 status = U_ZERO_ERROR;
156 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
157 TEST_STATUS(status, U_ZERO_ERROR);
158 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
159 TEST_STATUS(status, U_ZERO_ERROR);
160
161
162 /* After reinitializing ICU, we can not set the memory funcs again. */
163 status = U_ZERO_ERROR;
164 u_setDataDirectory(icuDataDir);
165 u_init(&status);
166 TEST_STATUS(status, U_ZERO_ERROR);
167
168 /* Doing ICU operations should cause allocations to come through our test heap */
169 gBlockCount = 0;
170 status = U_ZERO_ERROR;
171 rb = ures_open(NULL, "es", &status);
172 TEST_STATUS(status, U_ZERO_ERROR);
173 if (gBlockCount == 0) {
174 log_err("Heap functions are not being called from ICU.\n");
175 }
176 ures_close(rb);
177
178 /* Cleanup should put the heap back to its default implementation. */
179 ctest_resetICU();
180 u_getUnicodeVersion(unicodeVersion);
181 if (unicodeVersion[0] <= 0) {
182 log_err("Properties doesn't reinitialize without u_init.\n");
183 }
184 status = U_ZERO_ERROR;
185 u_init(&status);
186 TEST_STATUS(status, U_ZERO_ERROR);
187
188 /* ICU operations should no longer cause allocations to come through our test heap */
189 gBlockCount = 0;
190 status = U_ZERO_ERROR;
191 rb = ures_open(NULL, "fr", &status);
192 TEST_STATUS(status, U_ZERO_ERROR);
193 if (gBlockCount != 0) {
194 log_err("Heap functions did not reset after u_cleanup.\n");
195 }
196 ures_close(rb);
197 free(icuDataDir);
198
199 ctest_resetICU();
200 }
201
202
203