1 /*
   2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "jfr/recorder/access/jfrMemorySizer.hpp"
  27 #include "logging/log.hpp"
  28 #include "runtime/os.hpp"
  29 
  30 const julong MAX_ADJUSTED_GLOBAL_BUFFER_SIZE = 1 * M;
  31 const julong MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF = 512 * K;
  32 const julong MIN_GLOBAL_BUFFER_SIZE = 64 * K;
  33 // implies at least 2 * MIN_GLOBAL_BUFFER SIZE
  34 const julong MIN_BUFFER_COUNT = 2;
  35 // MAX global buffer count open ended
  36 const julong DEFAULT_BUFFER_COUNT = 20;
  37 // MAX thread local buffer size == size of a single global buffer (runtime determined)
  38 // DEFAULT thread local buffer size = 2 * os page size (runtime determined)
  39 const julong MIN_THREAD_BUFFER_SIZE = 4 * K;
  40 const julong MIN_MEMORY_SIZE = 1 * M;
  41 const julong DEFAULT_MEMORY_SIZE = 10 * M;
  42 
  43 //
  44 // In pages:
  45 //
  46 // units = total_pages / per_unit_pages
  47 //
  48 static julong div_pages(julong& total_pages, julong& per_unit_pages) {
  49   assert(total_pages > 0, "invariant");
  50   assert(per_unit_pages > 0, "invariant");
  51   assert(total_pages >= per_unit_pages, "invariant");
  52 
  53   const julong units = total_pages / per_unit_pages;
  54   const julong rem = total_pages % per_unit_pages;
  55 
  56   assert(units > 0, "invariant");
  57 
  58   if (rem > 0) {
  59     total_pages -= rem % units;
  60     per_unit_pages += rem / units;
  61   }
  62 
  63   assert(per_unit_pages > 0, "invariant");
  64   assert(total_pages % units == 0, "invariant");
  65   assert(units * per_unit_pages == total_pages, "invariant");
  66   assert(units == total_pages / per_unit_pages, "invariant");
  67 
  68   return units;
  69 }
  70 
  71 static void page_size_align_up(julong& value) {
  72   static const julong alignment = os::vm_page_size() - 1;
  73   value = (value + alignment) & ~alignment;
  74 }
  75 
  76 //
  77 // In bytes:
  78 // units = total_bytes / per_unit_bytes
  79 //
  80 static julong div_total_by_per_unit(julong& total_bytes, julong& per_unit_bytes) {
  81   assert(total_bytes > 0, "invariant");
  82   assert(per_unit_bytes > 0, "invariant");
  83   assert(total_bytes >= per_unit_bytes, "invariant");
  84 
  85   page_size_align_up(total_bytes);
  86   assert(total_bytes % os::vm_page_size() == 0, "invariant");
  87   julong total_pages = total_bytes / os::vm_page_size();
  88 
  89   page_size_align_up(per_unit_bytes);
  90   assert(per_unit_bytes % os::vm_page_size() == 0, "invariant");
  91   julong per_unit_pages = per_unit_bytes / os::vm_page_size();
  92 
  93   const julong units = div_pages(total_pages, per_unit_pages);
  94   assert(units > 0, "invariant");
  95 
  96   total_bytes = total_pages * os::vm_page_size();
  97   per_unit_bytes = per_unit_pages * os::vm_page_size();
  98 
  99   assert(per_unit_bytes > 0, "invariant");
 100   assert(total_bytes / per_unit_bytes == units, "invariant");
 101 
 102   return units;
 103 }
 104 
 105 //
 106 // per_unit_bytes = total_bytes / units
 107 //
 108 static julong div_total_by_units(julong& total_bytes, julong& units) {
 109   page_size_align_up(total_bytes);
 110   assert(total_bytes % os::vm_page_size() == 0, "invariant");
 111   julong total_pages = total_bytes / os::vm_page_size();
 112   assert(units > 0, "invariant");
 113 
 114   julong per_unit_pages = total_pages <= units ? 1 : total_pages / units;
 115   units = div_pages(total_pages, per_unit_pages);
 116 
 117   julong per_unit_bytes = per_unit_pages * os::vm_page_size();
 118   assert(per_unit_bytes % os::vm_page_size() == 0, "invariant");
 119 
 120   total_bytes = total_pages * os::vm_page_size();
 121   assert(total_bytes % os::vm_page_size() == 0, "invariant");
 122 
 123   assert(total_bytes % units == 0, "invariant");
 124   assert(total_bytes / units == per_unit_bytes, "invariant");
 125   assert(units * per_unit_bytes == total_bytes, "invariant");
 126 
 127   return per_unit_bytes;
 128 }
 129 
 130 //
 131 // total_bytes = per_unit_bytes * units;
 132 //
 133 static julong multiply(julong& per_unit_bytes, julong& units) {
 134   page_size_align_up(per_unit_bytes);
 135   assert(per_unit_bytes % os::vm_page_size() == 0, "invariant");
 136   assert(units > 0, "invariant");
 137 
 138   julong total_bytes = per_unit_bytes * units;
 139   assert(total_bytes % os::vm_page_size() == 0, "invariant");
 140 
 141   assert(total_bytes % units == 0, "invariant");
 142   assert(total_bytes / units == per_unit_bytes, "invariant");
 143   assert(units * per_unit_bytes == total_bytes, "invariant");
 144 
 145   return total_bytes;
 146 }
 147 
 148 // Total_bytes is explicitly set.
 149 //
 150 // Deduce other parameters by delegating to a sizing policy
 151 template <typename SizingPolicy>
 152 static julong adjust(JfrMemoryOptions* options) {
 153   page_size_align_up(options->memory_size);
 154   assert(options->memory_size % os::vm_page_size() == 0, "invariant");
 155   julong total_pages = options->memory_size / os::vm_page_size();
 156   assert(options->buffer_count > 0, "invariant");
 157   julong per_unit_pages = total_pages / options->buffer_count;
 158   page_size_align_up(options->thread_buffer_size);
 159   assert(options->thread_buffer_size % os::vm_page_size() == 0, "invariant");
 160   julong thread_buffer_pages = options->thread_buffer_size / os::vm_page_size();
 161 
 162   SizingPolicy::adjust(total_pages, per_unit_pages, options->buffer_count, thread_buffer_pages, options->thread_buffer_size_configured);
 163   assert(options->buffer_count * per_unit_pages == total_pages, "invariant");
 164 
 165   const julong per_unit_bytes = per_unit_pages * os::vm_page_size();
 166   options->memory_size = total_pages * os::vm_page_size();
 167   options->thread_buffer_size = thread_buffer_pages * os::vm_page_size();
 168 
 169   assert(options->memory_size % options->buffer_count == 0, "invariant");
 170   assert(options->memory_size / options->buffer_count == per_unit_bytes, "invariant");
 171   assert(options->buffer_count * per_unit_bytes == options->memory_size, "invariant");
 172   assert(per_unit_bytes >= options->thread_buffer_size, "invariant");
 173   return per_unit_bytes;
 174 }
 175 
 176 static void align_buffer_size(julong& buffer_size_in_pages, julong max_size_pages, julong min_size_pages, bool sizeup = false) {
 177   buffer_size_in_pages = MIN2(buffer_size_in_pages, max_size_pages);
 178   buffer_size_in_pages = MAX2(buffer_size_in_pages, min_size_pages);
 179   size_t multiples = 0;
 180   if (buffer_size_in_pages < max_size_pages) {
 181     while (buffer_size_in_pages >=
 182       (min_size_pages << (multiples + (sizeup ? 0 : 1)))) {
 183       ++multiples;
 184     }
 185     buffer_size_in_pages = min_size_pages << multiples;
 186   }
 187   assert(buffer_size_in_pages >= min_size_pages && buffer_size_in_pages <= max_size_pages, "invariant");
 188 }
 189 
 190 static void adjust_buffer_size_to_total_memory_size(julong& total_pages, julong& buffer_size_pages) {
 191   static const julong max_buffer_size_pages = MAX_ADJUSTED_GLOBAL_BUFFER_SIZE / os::vm_page_size();
 192   // If memory size is less than DEFAULT_MEMORY_SIZE,
 193   // the adjustment algorithm can decrease the size of the global buffer
 194   // all the way down to the MIN_GLOBAL_BUFFER_SIZE (taking embedded use case in account).
 195   // If memory size is larger than DEFAULT_MEMORY_SIZE, the lowest size of
 196   // a global buffer will be the size of MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF
 197   static const julong min_buffer_size_pages =
 198     total_pages * os::vm_page_size() < DEFAULT_MEMORY_SIZE ?
 199       MIN_GLOBAL_BUFFER_SIZE / os::vm_page_size() :
 200       MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF / os::vm_page_size();
 201 
 202   align_buffer_size(buffer_size_pages, max_buffer_size_pages, min_buffer_size_pages);
 203   assert(buffer_size_pages % min_buffer_size_pages == 0, "invariant");
 204 
 205   julong remainder = total_pages % buffer_size_pages;
 206   while (remainder >= (buffer_size_pages >> 1)) {
 207     if (buffer_size_pages <= min_buffer_size_pages) {
 208       break;
 209     }
 210     buffer_size_pages >>= 1;
 211     remainder = total_pages % buffer_size_pages;
 212   }
 213 }
 214 
 215 // Sizing policy class
 216 class ScaleOutAdjuster : public AllStatic {
 217  public:
 218   static void adjust(julong& total_pages,
 219                      julong& buffer_size_pages,
 220                      julong& buffer_count,
 221                      julong& thread_buffer_size_pages,
 222                      bool is_thread_buffer_size_set) {
 223     assert(buffer_count > 0, "invariant");
 224     adjust_buffer_size_to_total_memory_size(total_pages, buffer_size_pages);
 225     assert(buffer_size_pages * os::vm_page_size() >= MIN_GLOBAL_BUFFER_SIZE, "invariant");
 226     assert((buffer_size_pages * os::vm_page_size()) % MIN_GLOBAL_BUFFER_SIZE == 0, "invariant");
 227     if (is_thread_buffer_size_set) {
 228       if (thread_buffer_size_pages > buffer_size_pages) {
 229         buffer_size_pages = thread_buffer_size_pages;
 230       }
 231     }
 232     // and with this information, calculate what the new buffer count will be
 233     buffer_count = div_pages(total_pages, buffer_size_pages);
 234   }
 235 };
 236 
 237 static void memory_and_thread_buffer_size(JfrMemoryOptions* options) {
 238   assert(options->memory_size_configured, "invariant");
 239   assert(!options->buffer_count_configured, "invariant");
 240   assert(!options->global_buffer_size_configured, "invariant");
 241   // here the only thing specified is the overall total memory size
 242   // we can and will apply some sizing heuristics to derive both
 243   // the size of an individual global buffer and by implication the number of global
 244   // buffers to use. Starting values for buffer count and global_buffer_size
 245   // will be the defaults.
 246   options->global_buffer_size = adjust<ScaleOutAdjuster>(options);
 247 }
 248 
 249 static void memory_size_and_buffer_count(JfrMemoryOptions* options) {
 250   assert(options->memory_size_configured, "invariant");
 251   assert(!options->global_buffer_size_configured, "invariant");
 252   assert(!options->thread_buffer_size_configured, "invariant");
 253   assert(options->buffer_count_configured, "invariant");
 254   options->global_buffer_size = div_total_by_units(options->memory_size, options->buffer_count);
 255 }
 256 
 257 static void memory_size_and_global_buffer_size(JfrMemoryOptions* options) {
 258   assert(options->memory_size_configured, "invariant");
 259   assert(options->global_buffer_size_configured, "invariant");
 260   assert(!options->buffer_count_configured, "invariant");
 261   page_size_align_up(options->thread_buffer_size);
 262   options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
 263   if (options->thread_buffer_size > options->global_buffer_size) {
 264     options->global_buffer_size = options->thread_buffer_size;
 265     options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
 266   }
 267   assert(options->global_buffer_size >= options->thread_buffer_size, "invariant");
 268 }
 269 
 270 static bool is_ambiguous(const JfrMemoryOptions* options) {
 271   assert(options->memory_size_configured, "invariant");
 272   assert(options->global_buffer_size_configured, "invariant");
 273   assert(options->buffer_count_configured, "invariant");
 274   assert(options->thread_buffer_size <= options->global_buffer_size, "invariant");
 275   // This can cause an ambiguous situation because all three parameters are explicitly set.
 276   return options->global_buffer_size * options->buffer_count != options->memory_size;
 277 }
 278 
 279 static void all_options_set(JfrMemoryOptions* options) {
 280   options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
 281   page_size_align_up(options->thread_buffer_size);
 282   if (options->thread_buffer_size > options->global_buffer_size) {
 283     options->global_buffer_size = options->thread_buffer_size;
 284     options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
 285   }
 286   assert(options->global_buffer_size >= options->thread_buffer_size, "invariant");
 287   assert(options->memory_size / options->global_buffer_size == options->buffer_count, "invariant");
 288   assert(options->memory_size % options->global_buffer_size == 0, "invariant");
 289 }
 290 
 291 static void global_buffer_size(JfrMemoryOptions* options) {
 292   assert(!options->memory_size_configured, "invariant");
 293   page_size_align_up(options->thread_buffer_size);
 294   if (options->thread_buffer_size > options->global_buffer_size) {
 295     options->global_buffer_size = options->thread_buffer_size;
 296   }
 297   options->memory_size = multiply(options->global_buffer_size, options->buffer_count);
 298   assert(options->global_buffer_size >= options->thread_buffer_size, "invariant");
 299 }
 300 
 301 static void thread_buffer_size(JfrMemoryOptions* options) {
 302   assert(!options->global_buffer_size_configured, "invariant");
 303   assert(options->thread_buffer_size_configured, "invariant");
 304   page_size_align_up(options->thread_buffer_size);
 305   options->global_buffer_size = div_total_by_units(options->memory_size, options->buffer_count);
 306   if (options->thread_buffer_size > options->global_buffer_size) {
 307     options->global_buffer_size = options->thread_buffer_size;
 308     options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
 309   }
 310   assert(options->global_buffer_size >= options->thread_buffer_size, "invariant");
 311 }
 312 
 313 static void default_size(const JfrMemoryOptions* options) {
 314   // no memory options explicitly set
 315   // default values already statically adjusted
 316   assert(!options->thread_buffer_size_configured, "invariant");
 317   assert(!options->memory_size_configured, "invariant");
 318   assert(!options->buffer_count_configured, "invarinat");
 319   assert(!options->global_buffer_size_configured, "invariant");
 320 }
 321 
 322 #ifdef ASSERT
 323 static void assert_post_condition(const JfrMemoryOptions* options) {
 324   assert(options->memory_size % os::vm_page_size() == 0, "invariant");
 325   assert(options->global_buffer_size % os::vm_page_size() == 0, "invariant");
 326   assert(options->thread_buffer_size % os::vm_page_size() == 0, "invariant");
 327   assert(options->buffer_count > 0, "invariant");
 328 }
 329 #endif
 330 
 331 // MEMORY SIZING ALGORITHM
 332 
 333 bool JfrMemorySizer::adjust_options(JfrMemoryOptions* options) {
 334   assert(options != NULL, "invariant");
 335 
 336   enum MemoryOptions {
 337     MEMORY_SIZE = 1,
 338     GLOBAL_BUFFER_SIZE = 2,
 339     GLOBAL_BUFFER_COUNT = 4,
 340     THREAD_BUFFER_SIZE = 8
 341   };
 342 
 343   // LEGEND
 344   //
 345   // M = "memorysize" option
 346   // G = "globalbuffersize" option
 347   // C = "numglobalbuffers" option
 348   // T = "threadbuffersize" option
 349   //
 350   // The memory options comprise an n-set (a 4-set) = { M, G, C, T }
 351   //
 352   // Number of r-subsets = 5 (0, 1, 2, 3, 4) (including null set)
 353   //
 354   // Unordered selection:
 355   //
 356   // C(4, 0) = {} = NULL set = 1
 357   // C(4, 1) = { (M), (G), (C), (T) } = 4
 358   // C(4, 2) = { (M, G), (M, C), (M, T), (G, C), (G, T), (C, T) } = 6
 359   // C(4, 3) = { (M, G, C), (M, G, T), (M, C, T), (G, C, T) } = 4
 360   // C(4, 4) = { (M, G, C, T) } = 1
 361   //
 362   // in shorter terms: P({ M, G, C, T}) = 16
 363   //
 364 #define MG   (MEMORY_SIZE | GLOBAL_BUFFER_SIZE)
 365 #define MC   (MEMORY_SIZE | GLOBAL_BUFFER_COUNT)
 366 #define MT   (MEMORY_SIZE | THREAD_BUFFER_SIZE)
 367 #define MGC  (MG | GLOBAL_BUFFER_COUNT)
 368 #define MGT  (MG | THREAD_BUFFER_SIZE)
 369 #define MCT  (MC | THREAD_BUFFER_SIZE)
 370 #define MGCT (MGC | THREAD_BUFFER_SIZE)
 371 #define GC   (GLOBAL_BUFFER_SIZE | GLOBAL_BUFFER_COUNT)
 372 #define GT   (GLOBAL_BUFFER_SIZE | THREAD_BUFFER_SIZE)
 373 #define GCT  (GC | THREAD_BUFFER_SIZE)
 374 #define CT   (GLOBAL_BUFFER_COUNT | THREAD_BUFFER_SIZE)
 375 
 376   int set_of_options = 0;
 377 
 378   if (options->memory_size_configured) {
 379     set_of_options |= MEMORY_SIZE;
 380   }
 381   if (options->global_buffer_size_configured) {
 382     set_of_options |= GLOBAL_BUFFER_SIZE;
 383   }
 384   if (options->buffer_count_configured) {
 385     set_of_options |= GLOBAL_BUFFER_COUNT;
 386   }
 387   if (options->thread_buffer_size_configured) {
 388     set_of_options |= THREAD_BUFFER_SIZE;
 389   }
 390 
 391   switch (set_of_options) {
 392     case MT:
 393     case MEMORY_SIZE:
 394       memory_and_thread_buffer_size(options);
 395       break;
 396     case MC:
 397       memory_size_and_buffer_count(options);
 398       break;
 399     case MGT:
 400       assert(options->thread_buffer_size_configured, "invariant");
 401     case MG:
 402       memory_size_and_global_buffer_size(options);
 403       break;
 404     case MGC:
 405     case MGCT:
 406       if (is_ambiguous(options)) {
 407         // Let the user resolve the ambiguity by bailing.
 408         return false;
 409       }
 410       all_options_set(options);
 411       break;
 412     case GCT:
 413       assert(options->buffer_count_configured, "invariant");
 414       assert(options->thread_buffer_size_configured, "invariant");
 415     case GC:
 416       assert(options->global_buffer_size_configured, "invariant");
 417     case GT:
 418     case GLOBAL_BUFFER_COUNT:
 419     case GLOBAL_BUFFER_SIZE:
 420       global_buffer_size(options);
 421       break;
 422     case MCT:
 423       assert(options->memory_size_configured, "invariant");
 424     case CT:
 425       assert(options->buffer_count_configured, "invariant");
 426     case THREAD_BUFFER_SIZE:
 427       thread_buffer_size(options);
 428       break;
 429     default:
 430       default_size(options);
 431   }
 432   DEBUG_ONLY(assert_post_condition(options);)
 433   return true;
 434 }