Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/gc_verbose_old/VerboseFileLoggingOutput.cpp
5985 views
1
2
/*******************************************************************************
3
* Copyright (c) 1991, 2019 IBM Corp. and others
4
*
5
* This program and the accompanying materials are made available under
6
* the terms of the Eclipse Public License 2.0 which accompanies this
7
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
8
* or the Apache License, Version 2.0 which accompanies this distribution and
9
* is available at https://www.apache.org/licenses/LICENSE-2.0.
10
*
11
* This Source Code may also be made available under the following
12
* Secondary Licenses when the conditions for such availability set
13
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
14
* General Public License, version 2 with the GNU Classpath
15
* Exception [1] and GNU General Public License, version 2 with the
16
* OpenJDK Assembly Exception [2].
17
*
18
* [1] https://www.gnu.org/software/classpath/license.html
19
* [2] http://openjdk.java.net/legal/assembly-exception.html
20
*
21
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
22
*******************************************************************************/
23
24
#include "modronnls.h"
25
26
#include "VerboseFileLoggingOutput.hpp"
27
#include "VerboseEvent.hpp"
28
#include "GCExtensions.hpp"
29
#include "VerboseBuffer.hpp"
30
#include "EnvironmentBase.hpp"
31
32
#include <string.h>
33
34
/**
35
* Create a new MM_VerboseFileLoggingOutput instance.
36
* @return Pointer to the new MM_VerboseFileLoggingOutput.
37
*/
38
MM_VerboseFileLoggingOutput *
39
MM_VerboseFileLoggingOutput::newInstance(MM_EnvironmentBase *env, char *filename, UDATA numFiles, UDATA numCycles)
40
{
41
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env->getOmrVM());
42
43
MM_VerboseFileLoggingOutput *agent = (MM_VerboseFileLoggingOutput *)extensions->getForge()->allocate(sizeof(MM_VerboseFileLoggingOutput), MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());
44
if(agent) {
45
new(agent) MM_VerboseFileLoggingOutput(env);
46
if(!agent->initialize(env, filename, numFiles, numCycles)) {
47
agent->kill(env);
48
agent = NULL;
49
}
50
}
51
return agent;
52
}
53
54
/**
55
* Initializes the MM_VerboseFileLoggingOutput instance.
56
* @return true on success, false otherwise
57
*/
58
bool
59
MM_VerboseFileLoggingOutput::initialize(MM_EnvironmentBase *env, const char *filename, UDATA numFiles, UDATA numCycles)
60
{
61
_numFiles = numFiles;
62
_numCycles = numCycles;
63
64
if((_numFiles > 0) && (_numCycles > 0)) {
65
_mode = rotating_files;
66
} else {
67
_mode = single_file;
68
}
69
70
if (!initializeTokens(env)) {
71
return false;
72
}
73
74
if (!initializeFilename(env, filename)) {
75
return false;
76
}
77
78
IDATA initialFile = findInitialFile(env);
79
if (initialFile < 0) {
80
return false;
81
}
82
_currentFile = initialFile;
83
84
if(!openFile(env)) {
85
return false;
86
}
87
88
if(NULL == (_buffer = MM_VerboseBuffer::newInstance(env, INITIAL_BUFFER_SIZE))) {
89
return false;
90
}
91
92
return true;
93
}
94
95
/**
96
* Tear down the structures managed by the MM_VerboseFileLoggingOutput.
97
* Tears down the verbose buffer.
98
*/
99
void
100
MM_VerboseFileLoggingOutput::tearDown(MM_EnvironmentBase *env)
101
{
102
PORT_ACCESS_FROM_ENVIRONMENT(env);
103
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env->getOmrVM());
104
105
if(NULL != _buffer) {
106
_buffer->kill(env);
107
}
108
109
j9str_free_tokens(_tokens);
110
extensions->getForge()->free(_filename);
111
}
112
113
/**
114
* Initialize the _tokens field.
115
* for backwards compatibility with Sovereign, alias %p to be the same as %pid
116
*/
117
bool
118
MM_VerboseFileLoggingOutput::initializeTokens(MM_EnvironmentBase *env)
119
{
120
PORT_ACCESS_FROM_ENVIRONMENT(env);
121
char pidBuffer[64];
122
123
_tokens = j9str_create_tokens(j9time_current_time_millis());
124
if (_tokens == NULL) {
125
return false;
126
}
127
128
if (sizeof(pidBuffer) < j9str_subst_tokens(pidBuffer, sizeof(pidBuffer), "%pid", _tokens)) {
129
return false;
130
}
131
132
if (j9str_set_token(PORTLIB, _tokens, "p", "%s", pidBuffer)) {
133
return false;
134
}
135
136
return true;
137
}
138
139
/**
140
* Initialize the _filename field based on filename.
141
*
142
* Since token substitution only supports tokens starting with %, all # characters in the
143
* filename will be replaced with %seq (unless the # is already preceded by an odd number
144
* of % signs, in which case it is replaced with seq).
145
*
146
* e.g. foo# --> foo%seq
147
* foo%# --> foo%seq
148
* foo%%# --> foo%%%seq
149
*
150
* If %seq or %# is not specified, and if rotating logs have been requested, ".%seq" is
151
* appended to the log name.
152
*
153
* If the resulting filename is too large to fit in the buffer, it is truncated.
154
*
155
* @param[in] env the current environment
156
* @param[in] filename the user specified filename
157
*
158
* @return true on success, false on failure
159
*/
160
bool
161
MM_VerboseFileLoggingOutput::initializeFilename(MM_EnvironmentBase *env, const char *filename)
162
{
163
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env->getOmrVM());
164
165
if (_mode == rotating_files) {
166
const char* read = filename;
167
168
/* count the number of hashes in the source string */
169
UDATA hashCount = 0;
170
for (read = filename; '\0' != *read; read++) {
171
if ('#' == *read) {
172
hashCount++;
173
}
174
}
175
176
/* allocate memory for the copied template filename */
177
UDATA nameLen = strlen(filename) + 1;
178
if (hashCount > 0) {
179
/* each # expands into %seq, so for each # add 3 to len */
180
nameLen += hashCount * (sizeof("seq") - 1);
181
} else {
182
/* if there are no hashes we may append .%seq to the end */
183
nameLen += sizeof(".%seq") - 1;
184
}
185
186
_filename = (char*)extensions->getForge()->allocate(nameLen, MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());
187
if (NULL == _filename) {
188
return false;
189
}
190
191
/* copy the original filename into the allocated memory, expanding #s to %seq */
192
bool foundSeq = false;
193
bool oddPercents = false;
194
char* write = _filename;
195
for (read = filename; '\0' != *read; read++) {
196
/* check to see if %seq appears in the source filename */
197
if (oddPercents && (0 == strncmp(read, "seq", 3))) {
198
foundSeq = true;
199
}
200
201
if ('#' == *read) {
202
strcpy(write, oddPercents ? "seq" : "%seq");
203
write += strlen(write);
204
} else {
205
*write++ = *read;
206
}
207
oddPercents = ('%' == *read) ? !oddPercents : false;
208
}
209
210
*write = '\0';
211
212
if ( (false == foundSeq) && (0 == hashCount) ) {
213
strcpy(write, ".%seq");
214
}
215
} else {
216
_filename = (char*)extensions->getForge()->allocate(strlen(filename) + 1, MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());
217
if (NULL == _filename) {
218
return false;
219
}
220
strcpy(_filename, filename);
221
}
222
223
return true;
224
}
225
226
/**
227
* Formats and output data.
228
* Called by events, formats the data passed into verbose form and outputs it.
229
* @param indent The current level of indentation.
230
* @param format String to format the data into.
231
*/
232
void
233
MM_VerboseFileLoggingOutput::formatAndOutput(J9VMThread *vmThread, UDATA indent, const char *format, ...)
234
{
235
char inputString[VGC_INPUT_STRING_SIZE];
236
char localBuf[VGC_INPUT_STRING_SIZE];
237
UDATA length;
238
va_list args;
239
240
MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment((OMR_VMThread *)vmThread->omrVMThread);
241
PORT_ACCESS_FROM_ENVIRONMENT(env);
242
243
localBuf[0] = '\0';
244
for(UDATA i=0; i < indent; i++) {
245
strcat(localBuf, VGC_INDENT_SPACER);
246
}
247
248
va_start(args, format);
249
j9str_vprintf(inputString, VGC_INPUT_STRING_SIZE - strlen(localBuf), format, args);
250
va_end(args);
251
252
strcat(localBuf, inputString);
253
strcat(localBuf, "\n");
254
length = strlen(localBuf);
255
256
if(NULL != _buffer) {
257
if(_buffer->add(env, localBuf)) {
258
/* Added successfully - return */
259
return;
260
}
261
}
262
263
/* If there's a problem with the buffering, we just write straight to the file or stderr */
264
if(-1 != _logFileDescriptor) {
265
j9file_write_text(_logFileDescriptor, localBuf, length);
266
} else {
267
j9file_write_text(J9PORT_TTY_ERR, localBuf, length);
268
}
269
}
270
271
/**
272
* Generate an expanded filename based on currentFile.
273
* The caller is responsible for freeing the returned memory.
274
*
275
* @param env the current thread
276
* @param currentFile the current file number to substitute into the filename template
277
*
278
* @return NULL on failure, allocated memory on success
279
*/
280
char*
281
MM_VerboseFileLoggingOutput::expandFilename(MM_EnvironmentBase *env, UDATA currentFile)
282
{
283
PORT_ACCESS_FROM_ENVIRONMENT(env);
284
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env->getOmrVM());
285
286
if (_mode == rotating_files) {
287
j9str_set_token(PORTLIB, _tokens, "seq", "%03zu", currentFile + 1); /* plus one so the filenames start from .001 instead of .000 */
288
}
289
290
UDATA len = j9str_subst_tokens(NULL, 0, _filename, _tokens);
291
char *filenameToOpen = (char*)extensions->getForge()->allocate(len, MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());
292
if (NULL != filenameToOpen) {
293
j9str_subst_tokens(filenameToOpen, len, _filename, _tokens);
294
}
295
return filenameToOpen;
296
297
}
298
299
/**
300
* Probe the file system for existing files. Determine
301
* the first number which is unused, or the number of the oldest
302
* file if all numbers are used.
303
* @return the first file number to use (starting at 0), or -1 on failure
304
*/
305
IDATA
306
MM_VerboseFileLoggingOutput::findInitialFile(MM_EnvironmentBase *env)
307
{
308
PORT_ACCESS_FROM_ENVIRONMENT(env);
309
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env->getOmrVM());
310
I_64 oldestTime = J9CONST64(0x7FFFFFFFFFFFFFFF); /* the highest possible time. */
311
IDATA oldestFile = 0;
312
313
if (_mode != rotating_files) {
314
/* nothing to do */
315
return 0;
316
}
317
318
for (UDATA currentFile = 0; currentFile < _numFiles; currentFile++) {
319
char *filenameToOpen = expandFilename(env, currentFile);
320
if (NULL == filenameToOpen) {
321
return -1;
322
}
323
324
I_64 thisTime = j9file_lastmod(filenameToOpen);
325
extensions->getForge()->free(filenameToOpen);
326
327
if (thisTime < 0) {
328
/* file doesn't exist, or some other problem reading the file */
329
oldestFile = currentFile;
330
break;
331
} else if (thisTime < oldestTime) {
332
oldestTime = thisTime;
333
oldestFile = currentFile;
334
}
335
}
336
337
return oldestFile;
338
}
339
340
/**
341
* Opens the file to log output to and prints the header.
342
* @return true on success, false otherwise
343
*/
344
bool
345
MM_VerboseFileLoggingOutput::openFile(MM_EnvironmentBase *env)
346
{
347
PORT_ACCESS_FROM_ENVIRONMENT(env);
348
J9JavaVM* javaVM = (J9JavaVM *)env->getOmrVM()->_language_vm;
349
MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(javaVM);
350
const char* version = javaVM->memoryManagerFunctions->omrgc_get_version(env->getOmrVM());
351
352
char *filenameToOpen = expandFilename(env, _currentFile);
353
if (NULL == filenameToOpen) {
354
return false;
355
}
356
357
_logFileDescriptor = j9file_open(filenameToOpen, EsOpenRead | EsOpenWrite | EsOpenCreate | EsOpenTruncate, 0666);
358
if(-1 == _logFileDescriptor) {
359
char *cursor = filenameToOpen;
360
/**
361
* This may have failed due to directories in the path not being available.
362
* Try to create these directories and attempt to open again before failing.
363
*/
364
while ( (cursor = strchr(++cursor, DIR_SEPARATOR)) != NULL ) {
365
*cursor = '\0';
366
j9file_mkdir(filenameToOpen);
367
*cursor = DIR_SEPARATOR;
368
}
369
370
/* Try again */
371
_logFileDescriptor = j9file_open(filenameToOpen, EsOpenRead | EsOpenWrite | EsOpenCreate | EsOpenTruncate, 0666);
372
if (-1 == _logFileDescriptor) {
373
j9nls_printf(PORTLIB, J9NLS_ERROR, J9NLS_GC_UNABLE_TO_OPEN_FILE, filenameToOpen);
374
extensions->getForge()->free(filenameToOpen);
375
return false;
376
}
377
}
378
379
extensions->getForge()->free(filenameToOpen);
380
381
j9file_printf(PORTLIB, _logFileDescriptor, VERBOSEGC_HEADER_TEXT_ALL, version);
382
383
return true;
384
}
385
386
/**
387
* Prints the footer and closes the file being logged to.
388
*/
389
void
390
MM_VerboseFileLoggingOutput::closeFile(MM_EnvironmentBase *env)
391
{
392
PORT_ACCESS_FROM_ENVIRONMENT(env);
393
394
if(-1 != _logFileDescriptor){
395
UDATA length = strlen(VERBOSEGC_FOOTER_TEXT "\n");
396
j9file_write_text(_logFileDescriptor, VERBOSEGC_FOOTER_TEXT "\n", length);
397
j9file_close(_logFileDescriptor);
398
_logFileDescriptor = -1;
399
}
400
}
401
402
/**
403
* Closes the agent's output stream.
404
*/
405
void
406
MM_VerboseFileLoggingOutput::closeStream(MM_EnvironmentBase *env)
407
{
408
closeFile(env);
409
}
410
411
/**
412
* Flushes the verbose buffer to the output stream.
413
* Also cycles the output files if necessary.
414
*/
415
void
416
MM_VerboseFileLoggingOutput::endOfCycle(J9VMThread *vmThread)
417
{
418
MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment((OMR_VMThread *)vmThread->omrVMThread);
419
PORT_ACCESS_FROM_ENVIRONMENT(env);
420
421
if(-1 == _logFileDescriptor) {
422
/* we open the file at the end of the cycle so can't have a final empty file at the end of a run */
423
openFile(env);
424
}
425
426
if(NULL != _buffer) {
427
if(-1 != _logFileDescriptor){
428
j9file_write_text(_logFileDescriptor, _buffer->contents(), _buffer->currentSize());
429
j9file_write_text(_logFileDescriptor, "\n", 1);
430
} else {
431
j9file_write_text(J9PORT_TTY_ERR, _buffer->contents(), _buffer->currentSize());
432
j9file_write_text(J9PORT_TTY_ERR, "\n", 1);
433
}
434
_buffer->reset();
435
}
436
437
if(rotating_files == _mode) {
438
_currentCycle = (_currentCycle + 1) % _numCycles;
439
if(0 == _currentCycle) {
440
closeFile(env);
441
_currentFile = (_currentFile + 1) % _numFiles;
442
}
443
}
444
}
445
446
/**
447
* Reconfigures the agent according to the parameters passed.
448
* Required for Dynamic verbose gc configuration.
449
* @param filename The name of the file or output stream to log to.
450
* @param fileCount The number of files to log to.
451
* @param iterations The number of gc cycles to log to each file.
452
*/
453
bool
454
MM_VerboseFileLoggingOutput::reconfigure(MM_EnvironmentBase *env, const char *filename, UDATA numFiles, UDATA numCycles)
455
{
456
closeFile(env);
457
return initialize(env, filename, numFiles, numCycles);
458
}
459
460