aboutsummaryrefslogtreecommitdiff
path: root/src/share/vm/runtime/perfMemory.cpp
blob: 0855b38c97f66a4ffe3927da2459afd81711ce19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#include "precompiled.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/arguments.hpp"
#include "runtime/java.hpp"
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "runtime/perfData.hpp"
#include "runtime/perfMemory.hpp"
#include "runtime/statSampler.hpp"
#include "utilities/globalDefinitions.hpp"

// Prefix of performance data file.
const char               PERFDATA_NAME[] = "hsperfdata";

// Add 1 for the '_' character between PERFDATA_NAME and pid. The '\0' terminating
// character will be included in the sizeof(PERFDATA_NAME) operation.
static const size_t PERFDATA_FILENAME_LEN = sizeof(PERFDATA_NAME) +
                                            UINT_CHARS + 1;

char*                    PerfMemory::_start = NULL;
char*                    PerfMemory::_end = NULL;
char*                    PerfMemory::_top = NULL;
size_t                   PerfMemory::_capacity = 0;
jint                     PerfMemory::_initialized = false;
PerfDataPrologue*        PerfMemory::_prologue = NULL;

void perfMemory_init() {

  if (!UsePerfData) return;

  PerfMemory::initialize();
}

void perfMemory_exit() {

  if (!UsePerfData) return;
  if (!PerfMemory::is_initialized()) return;

  // if the StatSampler is active, then we don't want to remove
  // resources it may be dependent on. Typically, the StatSampler
  // is disengaged from the watcher thread when this method is called,
  // but it is not disengaged if this method is invoked during a
  // VM abort.
  //
  if (!StatSampler::is_active())
    PerfDataManager::destroy();

  // remove the persistent external resources, if any. this method
  // does not unmap or invalidate any virtual memory allocated during
  // initialization.
  //
  PerfMemory::destroy();
}

void PerfMemory::initialize() {

  if (_prologue != NULL)
    // initialization already performed
    return;

  size_t capacity = align_size_up(PerfDataMemorySize,
                                  os::vm_allocation_granularity());

  if (PerfTraceMemOps) {
    tty->print("PerfDataMemorySize = " SIZE_FORMAT ","
               " os::vm_allocation_granularity = " SIZE_FORMAT ","
               " adjusted size = " SIZE_FORMAT "\n",
               PerfDataMemorySize,
               os::vm_allocation_granularity(),
               capacity);
  }

  // allocate PerfData memory region
  create_memory_region(capacity);

  if (_start == NULL) {

    // the PerfMemory region could not be created as desired. Rather
    // than terminating the JVM, we revert to creating the instrumentation
    // on the C heap. When running in this mode, external monitoring
    // clients cannot attach to and monitor this JVM.
    //
    // the warning is issued only in debug mode in order to avoid
    // additional output to the stdout or stderr output streams.
    //
    if (PrintMiscellaneous && Verbose) {
      warning("Could not create PerfData Memory region, reverting to malloc");
    }

    _prologue = NEW_C_HEAP_OBJ(PerfDataPrologue);
  }
  else {

    // the PerfMemory region was created as expected.

    if (PerfTraceMemOps) {
      tty->print("PerfMemory created: address = " INTPTR_FORMAT ","
                 " size = " SIZE_FORMAT "\n",
                 (void*)_start,
                 _capacity);
    }

    _prologue = (PerfDataPrologue *)_start;
    _end = _start + _capacity;
    _top = _start + sizeof(PerfDataPrologue);
  }

  assert(_prologue != NULL, "prologue pointer must be initialized");

#ifdef VM_LITTLE_ENDIAN
  _prologue->magic = (jint)0xc0c0feca;
  _prologue->byte_order = PERFDATA_LITTLE_ENDIAN;
#else
  _prologue->magic = (jint)0xcafec0c0;
  _prologue->byte_order = PERFDATA_BIG_ENDIAN;
#endif

  _prologue->major_version = PERFDATA_MAJOR_VERSION;
  _prologue->minor_version = PERFDATA_MINOR_VERSION;
  _prologue->accessible = 0;

  _prologue->entry_offset = sizeof(PerfDataPrologue);
  _prologue->num_entries = 0;
  _prologue->used = 0;
  _prologue->overflow = 0;
  _prologue->mod_time_stamp = 0;

  OrderAccess::release_store(&_initialized, 1);
}

void PerfMemory::destroy() {

  assert(_prologue != NULL, "prologue pointer must be initialized");

  if (_start != NULL && _prologue->overflow != 0) {

    // This state indicates that the contiguous memory region exists and
    // that it wasn't large enough to hold all the counters. In this case,
    // we output a warning message to the user on exit if the -XX:+Verbose
    // flag is set (a debug only flag). External monitoring tools can detect
    // this condition by monitoring the _prologue->overflow word.
    //
    // There are two tunables that can help resolve this issue:
    //   - increase the size of the PerfMemory with -XX:PerfDataMemorySize=<n>
    //   - decrease the maximum string constant length with
    //     -XX:PerfMaxStringConstLength=<n>
    //
    if (PrintMiscellaneous && Verbose) {
      warning("PerfMemory Overflow Occurred.\n"
              "\tCapacity = " SIZE_FORMAT " bytes"
              "  Used = " SIZE_FORMAT " bytes"
              "  Overflow = " INT32_FORMAT " bytes"
              "\n\tUse -XX:PerfDataMemorySize=<size> to specify larger size.",
              PerfMemory::capacity(),
              PerfMemory::used(),
              _prologue->overflow);
    }
  }

  if (_start != NULL) {

    // this state indicates that the contiguous memory region was successfully
    // and that persistent resources may need to be cleaned up. This is
    // expected to be the typical condition.
    //
    delete_memory_region();
  }

  _start = NULL;
  _end = NULL;
  _top = NULL;
  _prologue = NULL;
  _capacity = 0;
}

// allocate an aligned block of memory from the PerfData memory
// region. This method assumes that the PerfData memory region
// was aligned on a double word boundary when created.
//
char* PerfMemory::alloc(size_t size) {

  if (!UsePerfData) return NULL;

  MutexLocker ml(PerfDataMemAlloc_lock);

  assert(_prologue != NULL, "called before initialization");

  // check that there is enough memory for this request
  if ((_top + size) >= _end) {

    _prologue->overflow += (jint)size;

    return NULL;
  }

  char* result = _top;

  _top += size;

  assert(contains(result), "PerfData memory pointer out of range");

  _prologue->used = (jint)used();
  _prologue->num_entries = _prologue->num_entries + 1;

  return result;
}

void PerfMemory::mark_updated() {
  if (!UsePerfData) return;

  _prologue->mod_time_stamp = os::elapsed_counter();
}

// Returns the complete path including the file name of performance data file.
// Caller is expected to release the allocated memory.
char* PerfMemory::get_perfdata_file_path() {
  char* dest_file = NULL;

  if (PerfDataSaveFile != NULL) {
    // dest_file_name stores the validated file name if file_name
    // contains %p which will be replaced by pid.
    dest_file = NEW_C_HEAP_ARRAY(char, JVM_MAXPATHLEN);
    if(!Arguments::copy_expand_pid(PerfDataSaveFile, strlen(PerfDataSaveFile),
                                   dest_file, JVM_MAXPATHLEN)) {
      FREE_C_HEAP_ARRAY(char, dest_file);
      if (PrintMiscellaneous && Verbose) {
        warning("Invalid performance data file path name specified, "\
                "fall back to a default name");
      }
    } else {
      return dest_file;
    }
  }
  // create the name of the file for retaining the instrumentation memory.
  dest_file = NEW_C_HEAP_ARRAY(char, PERFDATA_FILENAME_LEN);
  jio_snprintf(dest_file, PERFDATA_FILENAME_LEN,
               "%s_%d", PERFDATA_NAME, os::current_process_id());

  return dest_file;
}