1 /*
   2  * Copyright (c) 2017, 2020, 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 "classfile/javaClasses.hpp"
  27 #include "classfile/symbolTable.hpp"
  28 #include "classfile/systemDictionary.hpp"
  29 #include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp"
  30 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
  31 #include "oops/oop.inline.hpp"
  32 #include "runtime/fieldDescriptor.inline.hpp"
  33 #include "runtime/thread.hpp"
  34 #include "utilities/ostream.hpp"
  35 
  36 static Symbol* symbol_size = NULL;
  37 
  38 ObjectDescriptionBuilder::ObjectDescriptionBuilder() {
  39   reset();
  40 }
  41 
  42 void ObjectDescriptionBuilder::write_int(jint value) {
  43   char buf[20];
  44   jio_snprintf(buf, sizeof(buf), "%d", value);
  45   write_text(buf);
  46 }
  47 
  48 void ObjectDescriptionBuilder::write_text(const char* text) {
  49   if (_index == sizeof(_buffer) - 2) {
  50     return;
  51   }
  52   while (*text != '\0' && _index < sizeof(_buffer) - 2) {
  53     _buffer[_index] = *text;
  54     _index++;
  55     text++;
  56   }
  57   assert(_index < sizeof(_buffer) - 1, "index should not exceed buffer size");
  58   // add ellipsis if we reached end
  59   if (_index == sizeof(_buffer) - 2) {
  60     _buffer[_index-3] = '.';
  61     _buffer[_index-2] = '.';
  62     _buffer[_index-1] = '.';
  63   }
  64   // terminate string
  65   _buffer[_index] = '\0';
  66 }
  67 
  68 void ObjectDescriptionBuilder::reset() {
  69   _index = 0;
  70   _buffer[0] = '\0';
  71 }
  72 
  73 void ObjectDescriptionBuilder::print_description(outputStream* out) {
  74   out->print("%s", (const char*)_buffer);
  75 }
  76 
  77 const char* ObjectDescriptionBuilder::description() {
  78   if (_buffer[0] == '\0') {
  79     return NULL;
  80   }
  81   const size_t len = strlen(_buffer);
  82   char* copy = NEW_RESOURCE_ARRAY(char, len + 1);
  83   assert(copy != NULL, "invariant");
  84   strncpy(copy, _buffer, len + 1);
  85   return copy;
  86 }
  87 
  88 ObjectSampleDescription::ObjectSampleDescription(oop object) :
  89   _object(object) {
  90 }
  91 
  92 void ObjectSampleDescription::ensure_initialized() {
  93   if (symbol_size == NULL) {
  94     symbol_size = SymbolTable::new_permanent_symbol("size");
  95   }
  96 }
  97 
  98 void ObjectSampleDescription::print_description(outputStream* out) {
  99   write_object_to_buffer();
 100   _description.print_description(out);
 101 }
 102 
 103 const char* ObjectSampleDescription::description() {
 104   write_object_to_buffer();
 105   return _description.description();
 106 }
 107 
 108 void ObjectSampleDescription::write_text(const char* text) {
 109   _description.write_text(text);
 110 }
 111 
 112 void ObjectSampleDescription::write_int(jint value) {
 113   _description.write_int(value);
 114 }
 115 
 116 void ObjectSampleDescription::write_object_to_buffer() {
 117   ensure_initialized();
 118   _description.reset();
 119   write_object_details();
 120 }
 121 
 122 void ObjectSampleDescription::write_object_details() {
 123   Klass* klass = _object->klass();
 124   Symbol* class_name = klass->name();
 125   jint size;
 126 
 127   if (_object->is_a(SystemDictionary::Class_klass())) {
 128     write_class_name();
 129     return;
 130   }
 131 
 132   if (_object->is_a(SystemDictionary::Thread_klass())) {
 133     write_thread_name();
 134     return;
 135   }
 136 
 137   if (_object->is_a(SystemDictionary::ThreadGroup_klass())) {
 138     write_thread_group_name();
 139     return;
 140   }
 141 
 142   if (read_int_size(&size)) {
 143     write_size(size);
 144     return;
 145   }
 146 }
 147 
 148 void ObjectSampleDescription::write_class_name() {
 149   assert(_object->is_a(SystemDictionary::Class_klass()), "invariant");
 150   const Klass* const k = java_lang_Class::as_Klass(_object);
 151   if (k == NULL) {
 152     // might represent a primitive
 153     const Klass* const ak = java_lang_Class::array_klass_acquire(_object);
 154     // If ak is NULL, this is most likely a mirror associated with a
 155     // jvmti redefine/retransform scratch klass. We can't get any additional
 156     // information from it.
 157     if (ak != NULL) {
 158       write_text(type2name(java_lang_Class::primitive_type(_object)));
 159     }
 160     return;
 161   }
 162 
 163   if (k->is_instance_klass()) {
 164     const InstanceKlass* ik = InstanceKlass::cast(k);
 165     if (ik->is_unsafe_anonymous() || ik->is_hidden()) {
 166       return;
 167     }
 168     const Symbol* name = ik->name();
 169     if (name != NULL) {
 170       write_text("Class Name: ");
 171       write_text(name->as_klass_external_name());
 172     }
 173   }
 174 }
 175 
 176 void ObjectSampleDescription::write_thread_group_name() {
 177   assert(_object->is_a(SystemDictionary::ThreadGroup_klass()), "invariant");
 178   const char* tg_name = java_lang_ThreadGroup::name(_object);
 179   if (tg_name != NULL) {
 180     write_text("Thread Group: ");
 181     write_text(tg_name);
 182   }
 183 }
 184 
 185 void ObjectSampleDescription::write_thread_name() {
 186   assert(_object->is_a(SystemDictionary::Thread_klass()), "invariant");
 187   oop name = java_lang_Thread::name(_object);
 188   if (name != NULL) {
 189     char* p = java_lang_String::as_utf8_string(name);
 190     if (p != NULL) {
 191       write_text("Thread Name: ");
 192       write_text(p);
 193     }
 194   }
 195 }
 196 
 197 void ObjectSampleDescription::write_size(jint size) {
 198   if (size >= 0) {
 199     write_text("Size: ");
 200     write_int(size);
 201   }
 202 }
 203 
 204 bool ObjectSampleDescription::read_int_size(jint* result_size) {
 205   fieldDescriptor fd;
 206   Klass* klass = _object->klass();
 207   if (klass->is_instance_klass()) {
 208     InstanceKlass* ik = InstanceKlass::cast(klass);
 209     if (ik->find_field(symbol_size, vmSymbols::int_signature(), false, &fd) != NULL) {
 210        jint size = _object->int_field(fd.offset());
 211        *result_size = size;
 212        return true;
 213     }
 214   }
 215   return false;
 216 }