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