1174891Sedwin/* 2174891Sedwin Title: Object size 3174891Sedwin 4174891Sedwin Copyright (c) 2000 5174891Sedwin Cambridge University Technical Services Limited 6174891Sedwin 7174891Sedwin Further development David C.J. Matthews 2016, 2017 8174891Sedwin 9174891Sedwin This library is free software; you can redistribute it and/or 10174891Sedwin modify it under the terms of the GNU Lesser General Public 11174891Sedwin License version 2.1 as published by the Free Software Foundation. 12174891Sedwin 13174891Sedwin This library is distributed in the hope that it will be useful, 14174891Sedwin but WITHOUT ANY WARRANTY; without even the implied warranty of 15174891Sedwin MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16174891Sedwin Lesser General Public License for more details. 17174891Sedwin 18174891Sedwin You should have received a copy of the GNU Lesser General Public 19174891Sedwin License along with this library; if not, write to the Free Software 20174891Sedwin Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21174891Sedwin 22174891Sedwin*/ 23174891Sedwin#ifdef HAVE_CONFIG_H 24174891Sedwin#include "config.h" 25174891Sedwin#elif defined(_WIN32) 26174891Sedwin#include "winconfig.h" 27174891Sedwin#else 28174891Sedwin#error "No configuration file" 29174891Sedwin#endif 30174891Sedwin 31174891Sedwin#ifdef HAVE_STDIO_H 32174891Sedwin#include <stdio.h> 33174891Sedwin#endif 34174891Sedwin 35174891Sedwin#ifdef HAVE_SYS_TYPES_H 36174891Sedwin#include <sys/types.h> 37174891Sedwin#endif 38174891Sedwin 39174891Sedwin#ifdef HAVE_STDLIB_H 40174891Sedwin#include <stdlib.h> 41174891Sedwin#endif 42174891Sedwin 43174891Sedwin#ifdef HAVE_STRING_H 44174891Sedwin#include <string.h> 45174891Sedwin#endif 46174891Sedwin 47174891Sedwin#ifdef HAVE_ASSERT_H 48174891Sedwin#include <assert.h> 49174891Sedwin#define ASSERT(x) assert(x) 50174891Sedwin#else 51174891Sedwin#define ASSERT(x) 52174891Sedwin#endif 53174891Sedwin 54174891Sedwin 55174891Sedwin#include "globals.h" 56174891Sedwin#include "arb.h" 57174891Sedwin#include "run_time.h" 58174891Sedwin#include "machine_dep.h" 59174891Sedwin#include "objsize.h" 60174891Sedwin#include "scanaddrs.h" 61174891Sedwin#include "polystring.h" 62174891Sedwin#include "save_vec.h" 63174891Sedwin#include "bitmap.h" 64174891Sedwin#include "memmgr.h" 65174891Sedwin#include "mpoly.h" 66174891Sedwin#include "processes.h" 67174891Sedwin#include "rtsentry.h" 68174891Sedwin 69174891Sedwinextern "C" { 70174891Sedwin POLYEXTERNALSYMBOL POLYUNSIGNED PolyObjSize(FirstArgument threadId, PolyWord obj); 71174891Sedwin POLYEXTERNALSYMBOL POLYUNSIGNED PolyShowSize(FirstArgument threadId, PolyWord obj); 72174891Sedwin POLYEXTERNALSYMBOL POLYUNSIGNED PolyObjProfile(FirstArgument threadId, PolyWord obj); 73174891Sedwin} 74174891Sedwin 75174891Sedwinextern FILE *polyStdout; 76174891Sedwin 77174891Sedwin#define MAX_PROF_LEN 100 // Profile lengths between 1 and this 78174891Sedwin 79174891Sedwinclass ProcessVisitAddresses: public ScanAddress 80174891Sedwin{ 81174891Sedwinpublic: 82174891Sedwin virtual POLYUNSIGNED ScanAddressAt(PolyWord *pt) { return ShowWord(*pt); } 83174891Sedwin virtual POLYUNSIGNED ScanCodeAddressAt(PolyObject **pt) { return ShowObject(*pt); } 84174891Sedwin virtual PolyObject *ScanObjectAddress(PolyObject *base); 85174891Sedwin 86174891Sedwin POLYUNSIGNED ShowWord(PolyWord w) { 87174891Sedwin if (w.IsTagged() || w == PolyWord::FromUnsigned(0)) 88174891Sedwin return 0; 89174891Sedwin else return ShowObject(w.AsObjPtr()); 90174891Sedwin } 91174891Sedwin POLYUNSIGNED ShowObject(PolyObject *p); 92174891Sedwin ProcessVisitAddresses(bool show); 93174891Sedwin ~ProcessVisitAddresses(); 94174891Sedwin 95174891Sedwin VisitBitmap *FindBitmap(PolyObject *p); 96174891Sedwin void ShowBytes(PolyObject *start); 97174891Sedwin void ShowCode(PolyObject *start); 98174891Sedwin void ShowWords(PolyObject *start); 99174891Sedwin 100174891Sedwin POLYUNSIGNED total_length; 101174891Sedwin bool show_size; 102174891Sedwin VisitBitmap **bitmaps; 103174891Sedwin unsigned nBitmaps; 104174891Sedwin // Counts of objects of each size for mutable and immutable data. 105174891Sedwin unsigned iprofile[MAX_PROF_LEN+1]; 106174891Sedwin unsigned mprofile[MAX_PROF_LEN+1]; 107174891Sedwin}; 108174891Sedwin 109174891SedwinProcessVisitAddresses::ProcessVisitAddresses(bool show) 110174891Sedwin{ 111174891Sedwin // Need to get the allocation lock here. Another thread 112174891Sedwin // could allocate new local areas resulting in gMem.nlSpaces 113174891Sedwin // and gMem.lSpaces changing under our feet. 114174891Sedwin PLocker lock(&gMem.allocLock); 115174891Sedwin 116174891Sedwin total_length = 0; 117174891Sedwin show_size = show; 118174891Sedwin 119174891Sedwin // Create a bitmap for each of the areas apart from the IO area 120174891Sedwin nBitmaps = (unsigned)(gMem.lSpaces.size()+gMem.pSpaces.size()+gMem.cSpaces.size()); // 121174891Sedwin bitmaps = new VisitBitmap*[nBitmaps]; 122174891Sedwin unsigned bm = 0; 123174891Sedwin for (std::vector<PermanentMemSpace*>::iterator i = gMem.pSpaces.begin(); i < gMem.pSpaces.end(); i++) 124174891Sedwin { 125174891Sedwin MemSpace *space = *i; 126174891Sedwin // Permanent areas are filled with objects from the bottom. 127174891Sedwin bitmaps[bm++] = new VisitBitmap(space->bottom, space->top); 128174891Sedwin } 129174891Sedwin for (std::vector<LocalMemSpace*>::iterator i = gMem.lSpaces.begin(); i < gMem.lSpaces.end(); i++) 130174891Sedwin { 131208986Sbz LocalMemSpace *space = *i; 132174891Sedwin bitmaps[bm++] = new VisitBitmap(space->bottom, space->top); 133174891Sedwin } 134174891Sedwin for (std::vector<CodeSpace *>::iterator i = gMem.cSpaces.begin(); i < gMem.cSpaces.end(); i++) 135174891Sedwin { 136174891Sedwin CodeSpace *space = *i; 137174891Sedwin bitmaps[bm++] = new VisitBitmap(space->bottom, space->top); 138174891Sedwin } 139174891Sedwin ASSERT(bm == nBitmaps); 140174891Sedwin 141174891Sedwin // Clear the profile counts. 142174891Sedwin for (unsigned i = 0; i < MAX_PROF_LEN+1; i++) 143174891Sedwin { 144174891Sedwin iprofile[i] = mprofile[i] = 0; 145174891Sedwin } 146174891Sedwin} 147174891Sedwin 148174891Sedwin 149174891SedwinProcessVisitAddresses::~ProcessVisitAddresses() 150174891Sedwin{ 151174891Sedwin if (bitmaps) 152174891Sedwin { 153174891Sedwin for (unsigned i = 0; i < nBitmaps; i++) 154174891Sedwin delete(bitmaps[i]); 155174891Sedwin delete[](bitmaps); 156174891Sedwin } 157174891Sedwin} 158174891Sedwin 159174891Sedwin// Return the bitmap corresponding to the address or NULL if it isn't there. 160174891SedwinVisitBitmap *ProcessVisitAddresses::FindBitmap(PolyObject *p) 161174891Sedwin{ 162174891Sedwin for (unsigned i = 0; i < nBitmaps; i++) 163174891Sedwin { 164174891Sedwin VisitBitmap *bm = bitmaps[i]; 165174891Sedwin if (bm->InRange((PolyWord*)p)) return bm; 166174891Sedwin } 167174891Sedwin return 0; 168174891Sedwin} 169174891Sedwin 170174891Sedwinvoid ProcessVisitAddresses::ShowBytes(PolyObject *start) 171174891Sedwin{ 172174891Sedwin POLYUNSIGNED bytes = start->Length() * sizeof(PolyWord); 173174891Sedwin char *array = (char *) start; 174174891Sedwin 175174891Sedwin putc('\n', polyStdout); 176174891Sedwin 177174891Sedwin if (start->IsMutable()) fprintf(polyStdout, "MUTABLE "); 178174891Sedwin 179174891Sedwin fprintf(polyStdout, "BYTES:%p:%" POLYUFMT "\n", array, bytes); 180174891Sedwin 181174891Sedwin POLYUNSIGNED i, n; 182174891Sedwin for (i = 0, n = 0; n < bytes; n++) 183174891Sedwin { 184174891Sedwin fprintf(polyStdout, "%02x ",array[n] & 0xff); 185174891Sedwin i++; 186174891Sedwin if (i == 16) 187174891Sedwin { 188174891Sedwin putc('\n', polyStdout); 189174891Sedwin i = 0; 190174891Sedwin } 191174891Sedwin } 192174891Sedwin 193174891Sedwin if (i != 0) putc('\n', polyStdout); 194174891Sedwin} 195174891Sedwin 196174891Sedwin#define MAXNAME 500 197174891Sedwin 198174891Sedwinvoid ProcessVisitAddresses::ShowCode(PolyObject *start) 199174891Sedwin{ 200174891Sedwin POLYUNSIGNED length = start->Length(); 201174891Sedwin 202174891Sedwin putc('\n', polyStdout); 203174891Sedwin if (start->IsMutable()) fprintf(polyStdout, "MUTABLE "); 204174891Sedwin 205174891Sedwin char buffer[MAXNAME+1]; 206174891Sedwin PolyWord *consts = start->ConstPtrForCode(); 207174891Sedwin PolyWord string = consts[0]; 208 209 if (string == TAGGED(0)) 210 strcpy(buffer, "<not-named>"); 211 else 212 (void) Poly_string_to_C(string, buffer, sizeof(buffer)); 213 214 fprintf(polyStdout, "CODE:%p:%" POLYUFMT " %s\n", start, length, buffer); 215 216 POLYUNSIGNED i, n; 217 for (i = 0, n = 0; n < length; n++) 218 { 219 if (i != 0) putc('\t', polyStdout); 220 221 fprintf(polyStdout, "%8p ", start->Get(n).AsObjPtr()); 222 i++; 223 if (i == 4) 224 { 225 putc('\n', polyStdout); 226 i = 0; 227 } 228 } 229 230 if (i != 0) putc('\n', polyStdout); 231} 232 233void ProcessVisitAddresses::ShowWords(PolyObject *start) 234{ 235 POLYUNSIGNED length = start->Length(); 236 237 putc('\n', polyStdout); 238 if (start->IsMutable()) fprintf(polyStdout, "MUTABLE "); 239 240 fprintf(polyStdout, "%s:%p:%" POLYUFMT "\n", 241 start->IsClosureObject() ? "CLOSURE" : "WORDS", start, length); 242 243 POLYUNSIGNED i, n; 244 for (i = 0, n = 0; n < length; ) 245 { 246 if (i != 0) 247 putc('\t', polyStdout); 248 249 if (start->IsClosureObject() && n == 0) 250 { 251 fprintf(polyStdout, "%8p ", *(PolyObject**)start); 252 n += sizeof(PolyObject*) / sizeof(PolyWord); 253 } 254 else 255 { 256 PolyWord p = start->Get(n); 257 if (p.IsTagged()) 258 fprintf(polyStdout, "%08" POLYUFMT " ", p.AsUnsigned()); 259 else fprintf(polyStdout, "%8p ", p.AsObjPtr()); 260 n++; 261 } 262 i++; 263 if (i == 4) 264 { 265 putc('\n', polyStdout); 266 i = 0; 267 } 268 } 269 270 if (i != 0) 271 putc('\n', polyStdout); 272} 273 274// This is called initially to print the top-level object. 275// Since we don't process stacks it probably doesn't get called elsewhere. 276PolyObject *ProcessVisitAddresses::ScanObjectAddress(PolyObject *base) 277{ 278 POLYUNSIGNED lengthWord = ShowWord(base); 279 if (lengthWord) 280 ScanAddressesInObject(base, lengthWord); 281 return base; 282} 283 284// Handle the normal case. Print the object at this word and 285// return true is it must be handled recursively. 286POLYUNSIGNED ProcessVisitAddresses::ShowObject(PolyObject *p) 287{ 288 VisitBitmap *bm = FindBitmap(p); 289 290 if (bm == 0) 291 { 292 fprintf(polyStdout, "Bad address " ZERO_X "%p found\n", p); 293 return 0; 294 } 295 296 /* Have we already visited this object? */ 297 if (bm->AlreadyVisited(p)) 298 return 0; 299 300 bm->SetVisited(p); 301 302 POLYUNSIGNED obj_length = p->Length(); 303 304 // Increment the appropriate size profile count. 305 if (p->IsMutable()) 306 { 307 if (obj_length > MAX_PROF_LEN) 308 mprofile[MAX_PROF_LEN]++; 309 else 310 mprofile[obj_length]++; 311 } 312 else 313 { 314 if (obj_length > MAX_PROF_LEN) 315 iprofile[MAX_PROF_LEN]++; 316 else 317 iprofile[obj_length]++; 318 } 319 320 total_length += obj_length + 1; /* total space needed for object */ 321 322 if (p->IsByteObject()) 323 { 324 if (show_size) 325 ShowBytes(p); 326 return 0; 327 } 328 else if (p->IsCodeObject()) 329 { 330 PolyWord *cp; 331 POLYUNSIGNED const_count; 332 p->GetConstSegmentForCode(cp, const_count); 333 334 if (show_size) 335 ShowCode(p); 336 337 return p->LengthWord(); // Process addresses in it. 338 } 339 else // Word or closure object 340 { 341 if (show_size) 342 ShowWords(p); 343 return p->LengthWord(); // Process addresses in it. 344 } 345} 346 347 348Handle ObjSize(TaskData *taskData, Handle obj) 349{ 350 ProcessVisitAddresses process(false); 351 process.ScanObjectAddress(obj->WordP()); 352 return Make_arbitrary_precision(taskData, process.total_length); 353} 354 355Handle ShowSize(TaskData *taskData, Handle obj) 356{ 357 ProcessVisitAddresses process(true); 358 process.ScanObjectAddress(obj->WordP()); 359 fflush(polyStdout); /* We need this for Windows at least. */ 360 return Make_arbitrary_precision(taskData, process.total_length); 361} 362 363static void printfprof(unsigned *counts) 364{ 365 for(unsigned i = 0; i < MAX_PROF_LEN+1; i++) 366 { 367 if (counts[i] != 0) 368 { 369 if (i == MAX_PROF_LEN) 370 fprintf(polyStdout, ">%d\t%u\n", MAX_PROF_LEN, counts[i]); 371 else 372 fprintf(polyStdout, "%d\t%u\n", i, counts[i]); 373 } 374 } 375} 376 377POLYUNSIGNED PolyObjSize(FirstArgument threadId, PolyWord obj) 378{ 379 TaskData *taskData = TaskData::FindTaskForId(threadId); 380 ASSERT(taskData != 0); 381 taskData->PreRTSCall(); 382 383 ProcessVisitAddresses process(false); 384 if (!obj.IsTagged()) process.ScanObjectAddress(obj.AsObjPtr()); 385 Handle result = Make_arbitrary_precision(taskData, process.total_length); 386 387 taskData->PostRTSCall(); 388 return result->Word().AsUnsigned(); 389} 390 391POLYUNSIGNED PolyShowSize(FirstArgument threadId, PolyWord obj) 392{ 393 TaskData *taskData = TaskData::FindTaskForId(threadId); 394 ASSERT(taskData != 0); 395 taskData->PreRTSCall(); 396 397 ProcessVisitAddresses process(true); 398 if (!obj.IsTagged()) process.ScanObjectAddress(obj.AsObjPtr()); 399 fflush(polyStdout); /* We need this for Windows at least. */ 400 Handle result = Make_arbitrary_precision(taskData, process.total_length); 401 402 taskData->PostRTSCall(); 403 return result->Word().AsUnsigned(); 404} 405 406POLYUNSIGNED PolyObjProfile(FirstArgument threadId, PolyWord obj) 407{ 408 TaskData *taskData = TaskData::FindTaskForId(threadId); 409 ASSERT(taskData != 0); 410 taskData->PreRTSCall(); 411 412 ProcessVisitAddresses process(false); 413 if (!obj.IsTagged()) process.ScanObjectAddress(obj.AsObjPtr()); 414 fprintf(polyStdout, "\nImmutable object sizes and counts\n"); 415 printfprof(process.iprofile); 416 fprintf(polyStdout, "\nMutable object sizes and counts\n"); 417 printfprof(process.mprofile); 418 fflush(polyStdout); /* We need this for Windows at least. */ 419 Handle result = Make_arbitrary_precision(taskData, process.total_length); 420 421 taskData->PostRTSCall(); 422 return result->Word().AsUnsigned(); 423} 424 425struct _entrypts objSizeEPT[] = 426{ 427 { "PolyObjSize", (polyRTSFunction)&PolyObjSize}, 428 { "PolyShowSize", (polyRTSFunction)&PolyShowSize}, 429 { "PolyObjProfile", (polyRTSFunction)&PolyObjProfile}, 430 431 { NULL, NULL} // End of list. 432}; 433