Ticket #14755: TestFileAppend.cpp

File TestFileAppend.cpp, 8.2 KB (added by AGMS, 5 years ago)

TestFileAppend program for appending to a file slowly, then reading it back to see if it got corrupted.

Line 
1/******************************************************************************
2 * $Header: /CommonBe/agmsmith/Programming/AppendStressTest/RCS/TestFileAppend.cpp,v 1.7 2018/12/07 21:04:42 agmsmith Exp $
3 *
4 * Stress test the Haiku file systems by repeatedly appending lots of stuff to
5 * a file. Then read it back and see if it has gone corrupt.
6 *
7 * In more detail, open the file for append, then repeat a few thousand times
8 * (number of times specified by a command line argument): write a
9 * pseudo-random number of bytes to the end of the file, wait a few seconds.
10 * Then reset the pseudo-random number generator and try reading back the same
11 * data.
12 *
13 * The contents of this source code file are MIT licensed.
14 *
15 * Copyright (c) 2018 by Alexander G. M. Smith.
16 *
17 * Permission is hereby granted, free of charge, to any person obtaining a copy
18 * of this software and associated documentation files (the "Software"), to
19 * deal in the Software without restriction, including without limitation the
20 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
21 * sell copies of the Software, and to permit persons to whom the Software is
22 * furnished to do so, subject to the following conditions:
23 *
24 * The above copyright notice and this permission notice shall be included in
25 * all copies or substantial portions of the Software.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
33 * IN THE SOFTWARE.
34 */
35
36/* Standard C Library. */
37
38#include <errno.h>
39#include <fcntl.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45/* BeOS Things */
46
47#include <OS.h> // For snooze().
48#include <SupportDefs.h> // For bigtime_t.
49
50/* Our global things. */
51
52const int MAX_BUFFER = 0x10000; /* Needs to be a power of 2. */
53char g_Buffer [MAX_BUFFER + 1];
54char g_CompareBuffer [MAX_BUFFER + 1];
55int g_FileHandle = -1;
56
57char g_FileName[128] = "/tmp/AppendTestFile"; // Will append a serial number.
58
59
60/* Replicate a source string to fill a destination string to a given length.
61Doesn't NUL terminate the destination. Returns the destination string or NULL
62if something went wrong (like empty source string or bad pointers). */
63
64char * strreplicate (char *dest, const char *src, int count)
65{
66 if (count < 0 || dest == NULL || src == NULL || src[0] == 0)
67 return NULL;
68
69 const char *SrcPntr = src;
70 char *DestPntr = dest;
71 char *EndPntr = dest + count;
72
73 while (DestPntr < EndPntr)
74 {
75 *DestPntr++ = *SrcPntr++;
76 if (*SrcPntr == 0)
77 SrcPntr = src;
78 }
79 return dest;
80}
81
82
83/* Express the cycle number as a string and repeat it to fill the buffer to a
84random size (from 0 to MAX_BUFFER-1). Return the random size used. */
85
86int FillBufferWithCycleNumber (int iCycle)
87{
88 char NumberString [32];
89 sprintf (NumberString, "%d ", iCycle);
90
91 int DataSize = random () & (MAX_BUFFER - 1);
92 strreplicate (g_Buffer, NumberString, DataSize);
93 if (DataSize > 0)
94 g_Buffer [DataSize - 1] = '\n';
95
96 return DataSize;
97}
98
99
100/* Opens the test file in append mode, writes a random number of bytes to it
101using the iCycle as content (replicates it as an ASCII string to fill
102g_Buffer), then closes the file. Returns 0 on success, an error code if
103something goes wrong. */
104
105int WriteAppendCycle (int iCycle)
106{
107 int ErrorCode = 0;
108
109 int AmountToWrite = FillBufferWithCycleNumber (iCycle);
110
111 ssize_t AmountWritten = write (g_FileHandle, g_Buffer, AmountToWrite);
112
113 if (AmountWritten != AmountToWrite)
114 {
115 ErrorCode = errno;
116 printf ("Write failed on cycle %d, error code %08X (%s).\n",
117 iCycle, ErrorCode, strerror (ErrorCode));
118 }
119
120 return ErrorCode;
121}
122
123
124/* Read the file to see if the contents match the written cycles. */
125
126int TestContents (int FileHandle, int CycleCount)
127{
128 int iCycle;
129 for (iCycle = 0; iCycle < CycleCount; iCycle++)
130 {
131 int AmountToRead = FillBufferWithCycleNumber (iCycle);
132
133 ssize_t AmountRead = read (FileHandle, g_CompareBuffer, AmountToRead);
134
135 if (AmountRead != AmountToRead)
136 {
137 int ErrorCode = errno;
138 printf ("Failed while reading cycle %d, error code %08X (%s).\n",
139 iCycle, ErrorCode, strerror (ErrorCode));
140 return ErrorCode;
141 }
142
143 if (memcmp (g_Buffer, g_CompareBuffer, AmountToRead) != 0)
144 {
145 printf ("Corruption detected! Contents different for cycle %d.\n",
146 iCycle);
147 return -1;
148 }
149 }
150 return 0;
151}
152
153
154int main (int argc, char **argv)
155{
156 // Fix up the file name to have the team number appended so it is unique.
157
158 thread_info MyThreadInfo;
159 get_thread_info (find_thread(NULL), &MyThreadInfo);
160 sprintf (g_FileName + strlen (g_FileName), "-%d", (int) MyThreadInfo.team);
161
162 if (argc < 2)
163 {
164 printf ("%s CycleCount [DelaySeconds]
165
166Stress test the appending of data to a file, since there seems to be a
167Haiku OS bug related to this in 2018. The command line argument specifies
168the number of write cycles to do. The file is opened in append mode, then
169repeated cycles write a pseudo-random number of bytes from 0 to %d.
170There's an optional delay between cycles, specified in seconds (can be a
171decimal fraction). Once all the cycles are done, it reopens the
172\"%s\" file and reads it back to see if it is correct.
173
174Written by Alexander G. M. Smith. MIT licensed.
175$Header: /CommonBe/agmsmith/Programming/AppendStressTest/RCS/TestFileAppend.cpp,v 1.7 2018/12/07 21:04:42 agmsmith Exp $
176Compiled on " __DATE__ " at " __TIME__ ".\n",
177 argv[0], MAX_BUFFER-1, g_FileName);
178 return 1;
179 }
180
181 int CycleCount = 0;
182 double DelaySeconds = 0.0;
183 bigtime_t DelayMicroseconds = 0;
184 int ErrorCode = 0;
185 int iCycle;
186
187 CycleCount = atoi (argv[1]);
188 if (CycleCount < 0)
189 CycleCount = 0;
190
191 if (argc >= 3)
192 DelaySeconds = atof (argv[2]);
193 if (DelaySeconds < 0.0)
194 DelaySeconds = 0.0;
195 DelayMicroseconds = bigtime_t (DelaySeconds * 1000000);
196
197 if (unlink (g_FileName) == 0)
198 printf ("Removed \"%s\" from previous run.\n", g_FileName);
199
200 srandom (1234567890); // Reset random number generator to start of sequence.
201
202 printf ("Doing %d append cycles. Waiting %0.6f seconds between cycles.\n",
203 CycleCount, DelaySeconds);
204
205 g_FileHandle = open (g_FileName, O_RDWR | O_APPEND | O_CREAT, 0644);
206 if (g_FileHandle < 0)
207 {
208 ErrorCode = errno;
209 printf (
210 "Unable to open file \"%s\" for appending, error code %08X (%s).\n",
211 g_FileName, ErrorCode, strerror (ErrorCode));
212 return 10;
213 }
214
215 for (iCycle = 0; iCycle < CycleCount; iCycle++)
216 {
217 snooze (DelayMicroseconds);
218 ErrorCode = WriteAppendCycle (iCycle);
219 if (ErrorCode != 0)
220 return 10;
221 write (STDOUT_FILENO, ".", 1); // Unbuffered, unlike printf.
222 }
223
224 close (g_FileHandle);
225
226 printf ("\nReading back the file to see if the contents are correct.\n");
227
228 g_FileHandle = open (g_FileName, O_RDONLY);
229 if (g_FileHandle < 0)
230 {
231 ErrorCode = errno;
232 printf (
233 "Unable to reopen file \"%s\" for reading, error code %08X (%s).\n",
234 g_FileName, ErrorCode, strerror (ErrorCode));
235 return 10;
236 }
237
238 srandom (1234567890);
239 ErrorCode = TestContents (g_FileHandle, CycleCount);
240
241 close (g_FileHandle);
242
243 if (ErrorCode != 0)
244 return 10;
245
246 printf ("Test was successful.\n");
247 return 0;
248}
249
250
251/******************************************************************************
252 * $Log: TestFileAppend.cpp,v $
253 * Revision 1.7 2018/12/07 21:04:42 agmsmith
254 * Append a serial number to the file name to make it easier to run
255 * multiple copies of the test.
256 *
257 * Revision 1.6 2018/12/05 21:11:01 agmsmith
258 * Now open the file for append just once at the beginning, not on
259 * every write cycle. That's how syslog_daemon does it in Haiku.
260 *
261 * Revision 1.5 2018/12/05 17:07:50 agmsmith
262 * Add a time delay between write cycles.
263 *
264 * Revision 1.4 2018/08/26 16:42:38 agmsmith
265 * First working version.
266 *
267 * Revision 1.3 2018/08/26 14:34:07 agmsmith
268 * Wrote the documentation.
269 *
270 * Revision 1.2 2017/09/28 17:04:44 agmsmith
271 * Added RCS headers to the source.
272 */