1 /*
   2  * Copyright (c) 2017, 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 "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/thread.hpp"
  33 #include "utilities/ostream.hpp"
  34 
  35 static Symbol* symbol_size = NULL;
  36 
  37 ObjectDescriptionBuilder::ObjectDescriptionBuilder() {
  38   reset();
  39 }
  40 
  41 void ObjectDescriptionBuilder::write_int(jint value) {
  42   char buf[20];
  43   jio_snprintf(buf, sizeof(buf), "%d", value);
  44   write_text(buf);
  45 }
  46 
  47 void ObjectDescriptionBuilder::write_text(const char* text) {
  48   if (_index == sizeof(_buffer) - 2) {
  49     return;
  50   }
  51   while (*text != '\0' && _index < sizeof(_buffer) - 2) {
  52     _buffer[_index] = *text;
  53     _index++;
  54     text++;
  55   }
  56   assert(_index < sizeof(_buffer) - 1, "index should not exceed buffer size");
  57   // add ellipsis if we reached end
  58   if (_index == sizeof(_buffer) - 2) {
  59     _buffer[_index-3] = '.';
  60     _buffer[_index-2] = '.';
  61     _buffer[_index-1] = '.';
  62   }
  63   // terminate string
  64   _buffer[_index] = '\0';
  65 }
  66 
  67 void ObjectDescriptionBuilder::reset() {
  68   _index = 0;
  69   _buffer[0] = '\0';
  70 }
  71 
  72 void ObjectDescriptionBuilder::print_description(outputStream* out) {
  73   out->print("%s", (const char*)_buffer);
  74 }
  75 
  76 const char* ObjectDescriptionBuilder::description() {
  77   if (_buffer[0] == '\0') {
  78     return NULL;
  79   }
  80   const size_t len = strlen(_buffer);
  81   char* copy = NEW_RESOURCE_ARRAY(char, len + 1);
  82   assert(copy != NULL, "invariant");
  83   strncpy(copy, _buffer, len + 1);
  84   return copy;
  85 }
  86 
  87 ObjectSampleDescription::ObjectSampleDescription(oop object) :
  88   _object(object) {
  89 }
  90 
  91 void ObjectSampleDescription::ensure_initialized() {
  92   if (symbol_size == NULL) {
  93     symbol_size = SymbolTable::new_permanent_symbol("size", Thread::current());
  94   }
  95 }
  96 
  97 void ObjectSampleDescription::print_description(outputStream* out) {
  98   write_object_to_buffer();
  99   _description.print_description(out);
 100 }
 101 
 102 const char* ObjectSampleDescription::description() {
 103   write_object_to_buffer();
 104   return _description.description();
 105 }
 106 
 107 void ObjectSampleDescription::write_text(const char* text) {
 108   _description.write_text(text);
 109 }
 110 
 111 void ObjectSampleDescription::write_int(jint value) {
 112   _description.write_int(value);
 113 }
 114 
 115 void ObjectSampleDescription::write_object_to_buffer() {
 116   ensure_initialized();
 117   _description.reset();
 118   write_object_details();
 119 }
 120 
 121 void ObjectSampleDescription::write_object_details() {
 122   Klass* klass = _object->klass();
 123   Symbol* class_name = klass->name();
 124   jint size;
 125 
 126   if (_object->is_a(SystemDictionary::Class_klass())) {
 127     write_class_name();
 128     return;
 129   }
 130 
 131   if (_object->is_a(SystemDictionary::Thread_klass())) {
 132     write_thread_name();
 133     return;
 134   }
 135 
 136   if (_object->is_a(SystemDictionary::ThreadGroup_klass())) {
 137     write_thread_group_name();
 138     return;
 139   }
 140 
 141   if (read_int_size(&size)) {
 142     write_size(size);
 143     return;
 144   }
 145 }
 146 
 147 void ObjectSampleDescription::write_class_name() {
 148   assert(_object->is_a(SystemDictionary::Class_klass()), "invariant");
 149   const Klass* const k = java_lang_Class::as_Klass(_object);
 150   if (k == NULL) {
 151     // might represent a primitive
 152     const Klass* const ak = java_lang_Class::array_klass_acquire(_object);
 153     // If ak is NULL, this is most likely a mirror associated with a
 154     // jvmti redefine/retransform scratch klass. We can't get any additional
 155     // information from it.
 156     if (ak != NULL) {
 157       write_text(type2name(java_lang_Class::primitive_type(_object)));
 158     }
 159     return;
 160   }
 161 
 162   if (k->is_instance_klass()) {
 163     const InstanceKlass* ik = InstanceKlass::cast(k);
 164     if (ik->is_anonymous()) {
 165       return;
 166     }
 167     assert(!ik->is_anonymous(), "invariant");
 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 }