* Re: [Emacs-diffs] /srv/bzr/emacs/trunk r109111: Simple free memory accounting feature.
2012-07-17 9:45 ` [Emacs-diffs] /srv/bzr/emacs/trunk r109111: Simple free memory accounting feature Stefan Monnier
@ 2012-07-17 13:40 ` Dmitry Antipov
0 siblings, 0 replies; 2+ messages in thread
From: Dmitry Antipov @ 2012-07-17 13:40 UTC (permalink / raw)
To: Stefan Monnier; +Cc: emacs-devel
[-- Attachment #1: Type: text/plain, Size: 1022 bytes --]
On 07/17/2012 01:45 PM, Stefan Monnier wrote:
> There are two problems, here:
> - if the data is unavailable, better return nil than 0.
> - we now have 2 ways to get the data, neither of which gives it all:
> garbage-collect returns details data but misses the new vector-bytes
> and bytes-free, whereas memory-free includes those 2 but only provides
> the combined value.
>
> The first problem is obviously a detail, but the second problem needs to
> be fixed. The way I see it, it should be fixed by improving
> garbage-collect's return value. The only problem with that is backward
> compatibility, but AFAIK there are only 2 users of that data:
> memory-usage and chart.el, which we can both fix easily.
> I suggest we make garbage-collect return its data in a new format
> that is more self-descriptive, like an alist where each entry takes the
> form (CATEGORY SIZE USED FREE), where CATEGORY would be a symbol (like
> `cons') and SIZE is the size of each element in bytes.
OK, look at this.
Dmitry
[-- Attachment #2: gc.patch --]
[-- Type: text/plain, Size: 19938 bytes --]
=== modified file 'lisp/emacs-lisp/chart.el'
--- lisp/emacs-lisp/chart.el 2012-05-13 03:05:06 +0000
+++ lisp/emacs-lisp/chart.el 2012-07-17 13:00:22 +0000
@@ -676,23 +676,25 @@
"Chart the current storage requirements of Emacs."
(interactive)
(let* ((data (garbage-collect))
- (names '("strings/2" "vectors"
- "conses" "free cons"
- "syms" "free syms"
- "markers" "free mark"
- ;; "floats" "free flt"
- ))
- (nums (list (/ (nth 3 data) 2)
- (nth 4 data)
- (car (car data)) ; conses
- (cdr (car data))
- (car (nth 1 data)) ; syms
- (cdr (nth 1 data))
- (car (nth 2 data)) ; markers
- (cdr (nth 2 data))
- ;(car (nth 5 data)) ; floats are Emacs only
- ;(cdr (nth 5 data))
- )))
+ (cons-info (nth 0 data))
+ (symbol-info (nth 1 data))
+ (misc-info (nth 2 data))
+ (string-info (nth 3 data))
+ (vector-info (nth 4 data))
+ (float-info (nth 5 data))
+ (interval-info (nth 6 data))
+ (buffer-info (nth 7 data))
+ (names '("conses" "symbols" "miscs" "strings"
+ "vectors" "floats" "intervals" "buffers"))
+ (nums (list (* (nth 1 cons-info) (nth 2 cons-info))
+ (* (nth 1 symbol-info) (nth 2 symbol-info))
+ (* (nth 1 misc-info) (nth 2 misc-info))
+ (+ (* (nth 1 string-info) (nth 2 string-info))
+ (nth 3 string-info))
+ (nth 3 vector-info)
+ (* (nth 1 float-info) (nth 2 float-info))
+ (* (nth 1 interval-info) (nth 2 interval-info))
+ (* (nth 1 buffer-info) (nth 2 buffer-info)))))
;; Let's create the chart!
(chart-bar-quickie 'vertical "Emacs Runtime Storage Usage"
names "Storage Items"
=== modified file 'src/alloc.c'
--- src/alloc.c 2012-07-17 12:31:29 +0000
+++ src/alloc.c 2012-07-17 13:37:24 +0000
@@ -187,11 +187,24 @@
int abort_on_gc;
-/* Number of live and free conses etc. */
-
-static EMACS_INT total_conses, total_markers, total_symbols, total_vector_size;
-static EMACS_INT total_free_conses, total_free_markers, total_free_symbols;
-static EMACS_INT total_free_floats, total_floats, total_free_vector_bytes;
+/* Number of live and free objects of the particular type. */
+
+static EMACS_INT total_conses, total_free_conses;
+static EMACS_INT total_symbols, total_free_symbols;
+static EMACS_INT total_miscs, total_free_miscs;
+static EMACS_INT total_intervals, total_free_intervals;
+static EMACS_INT total_floats, total_free_floats;
+static EMACS_INT total_buffers;
+
+/* Number of live strings, number of bytes
+ used by them, number of free strings. */
+
+static EMACS_INT total_strings, total_string_bytes, total_free_strings;
+
+/* Number of live vectors, amount of bytes used by
+ them, number of free bytes in vector blocks. */
+
+static EMACS_INT total_vectors, total_vector_bytes, total_free_vector_bytes;
/* Points to memory space allocated as "spare", to be freed if we run
out of memory. We keep one large block, four cons-blocks, and
@@ -1478,10 +1491,6 @@
static int interval_block_index = INTERVAL_BLOCK_SIZE;
-/* Number of free and live intervals. */
-
-static EMACS_INT total_free_intervals, total_intervals;
-
/* List of free intervals. */
static INTERVAL interval_free_list;
@@ -1521,6 +1530,7 @@
consing_since_gc += sizeof (struct interval);
intervals_consed++;
+ total_intervals++;
total_free_intervals--;
RESET_INTERVAL (val);
val->gcmarkbit = 0;
@@ -1702,14 +1712,6 @@
static struct Lisp_String *string_free_list;
-/* Number of live and free Lisp_Strings. */
-
-static EMACS_INT total_strings, total_free_strings;
-
-/* Number of bytes used by live strings. */
-
-static EMACS_INT total_string_size;
-
/* Given a pointer to a Lisp_String S which is on the free-list
string_free_list, return a pointer to its successor in the
free-list. */
@@ -1939,9 +1941,9 @@
MALLOC_UNBLOCK_INPUT;
- --total_free_strings;
- ++total_strings;
- ++strings_consed;
+ total_free_strings--;
+ total_strings++;
+ strings_consed++;
consing_since_gc += sizeof *s;
#ifdef GC_CHECK_STRING_BYTES
@@ -2067,6 +2069,7 @@
old_data->string = NULL;
}
+ total_string_bytes += needed;
consing_since_gc += needed;
}
@@ -2080,8 +2083,7 @@
struct string_block *live_blocks = NULL;
string_free_list = NULL;
- total_strings = total_free_strings = 0;
- total_string_size = 0;
+ total_strings = total_free_strings = total_string_bytes = 0;
/* Scan strings_blocks, free Lisp_Strings that aren't marked. */
for (b = string_blocks; b; b = next)
@@ -2107,7 +2109,7 @@
UNMARK_BALANCE_INTERVALS (s->intervals);
++total_strings;
- total_string_size += STRING_BYTES (s);
+ total_string_bytes += STRING_BYTES (s);
}
else
{
@@ -2636,6 +2638,7 @@
eassert (!FLOAT_MARKED_P (XFLOAT (val)));
consing_since_gc += sizeof (struct Lisp_Float);
floats_consed++;
+ total_floats++;
total_free_floats--;
return val;
}
@@ -2744,6 +2747,7 @@
XSETCDR (val, cdr);
eassert (!CONS_MARKED_P (XCONS (val)));
consing_since_gc += sizeof (struct Lisp_Cons);
+ total_conses++;
total_free_conses--;
cons_cells_consed++;
return val;
@@ -3069,6 +3073,14 @@
? vector->header.size & PSEUDOVECTOR_SIZE_MASK \
: vector->header.next.nbytes)
+/* Return the memory footprint of vector V in bytes.
+ Do not use for pseudovectors since the most of them
+ has something beyond Lisp objects at the beginning. */
+
+#define VECTOR_BYTES(v) \
+ (eassert (!((v)->header.size & PSEUDOVECTOR_FLAG)), \
+ (header_size + VECTOR_SIZE (v) * word_size))
+
/* Reclaim space used by unmarked vectors. */
static void
@@ -3077,7 +3089,7 @@
struct vector_block *block = vector_blocks, **bprev = &vector_blocks;
struct Lisp_Vector *vector, *next, **vprev = &large_vectors;
- total_free_vector_bytes = total_vector_size = 0;
+ total_vectors = total_free_vector_bytes = total_vector_bytes = 0;
memset (vector_free_lists, 0, sizeof (vector_free_lists));
/* Looking through vector blocks. */
@@ -3092,7 +3104,8 @@
if (VECTOR_MARKED_P (vector))
{
VECTOR_UNMARK (vector);
- total_vector_size += VECTOR_SIZE (vector);
+ total_vectors++;
+ total_vector_bytes += vector->header.next.nbytes;
next = ADVANCE (vector, vector->header.next.nbytes);
}
else
@@ -3148,7 +3161,10 @@
if (VECTOR_MARKED_P (vector))
{
VECTOR_UNMARK (vector);
- total_vector_size += VECTOR_SIZE (vector);
+ total_vectors++;
+ /* This code assumes that all pseudovectors are
+ small enough to be allocated from blocks. */
+ total_vector_bytes += VECTOR_BYTES (vector);
vprev = &vector->header.next.vector;
}
else
@@ -3198,7 +3214,8 @@
/* Back to a reasonable maximum of mmap'ed areas. */
mallopt (M_MMAP_MAX, MMAP_MAX_AREAS);
#endif
-
+ total_vectors++;
+ total_vector_bytes += nbytes;
consing_since_gc += nbytes;
vector_cells_consed += len;
}
@@ -3248,6 +3265,7 @@
XSETPVECTYPESIZE (b, PVEC_BUFFER, (offsetof (struct buffer, own_text)
- header_size) / word_size);
+ total_buffers++;
/* Note that the fields of B are not initialized. */
return b;
}
@@ -3492,6 +3510,7 @@
p->declared_special = 0;
consing_since_gc += sizeof (struct Lisp_Symbol);
symbols_consed++;
+ total_symbols++;
total_free_symbols--;
return val;
}
@@ -3499,7 +3518,7 @@
\f
/***********************************************************************
- Marker (Misc) Allocation
+ Misc Objects Allocation
***********************************************************************/
/* Like union Lisp_Misc, but padded so that its size is a multiple of
@@ -3517,20 +3536,20 @@
/* Allocation of markers and other objects that share that structure.
Works like allocation of conses. */
-#define MARKER_BLOCK_SIZE \
- ((1020 - sizeof (struct marker_block *)) / sizeof (union aligned_Lisp_Misc))
+#define MISC_BLOCK_SIZE \
+ ((1020 - sizeof (struct misc_block *)) / sizeof (union aligned_Lisp_Misc))
-struct marker_block
+struct misc_block
{
- /* Place `markers' first, to preserve alignment. */
- union aligned_Lisp_Misc markers[MARKER_BLOCK_SIZE];
- struct marker_block *next;
+ /* Place `miscs' first, to preserve alignment. */
+ union aligned_Lisp_Misc miscs[MISC_BLOCK_SIZE];
+ struct misc_block *next;
};
-static struct marker_block *marker_block;
-static int marker_block_index = MARKER_BLOCK_SIZE;
+static struct misc_block *misc_block;
+static int misc_block_index = MISC_BLOCK_SIZE;
-static union Lisp_Misc *marker_free_list;
+static union Lisp_Misc *misc_free_list;
/* Return a newly allocated Lisp_Misc object, with no substructure. */
@@ -3543,28 +3562,29 @@
MALLOC_BLOCK_INPUT;
- if (marker_free_list)
+ if (misc_free_list)
{
- XSETMISC (val, marker_free_list);
- marker_free_list = marker_free_list->u_free.chain;
+ XSETMISC (val, misc_free_list);
+ misc_free_list = misc_free_list->u_free.chain;
}
else
{
- if (marker_block_index == MARKER_BLOCK_SIZE)
+ if (misc_block_index == MISC_BLOCK_SIZE)
{
- struct marker_block *new = lisp_malloc (sizeof *new, MEM_TYPE_MISC);
- new->next = marker_block;
- marker_block = new;
- marker_block_index = 0;
- total_free_markers += MARKER_BLOCK_SIZE;
+ struct misc_block *new = lisp_malloc (sizeof *new, MEM_TYPE_MISC);
+ new->next = misc_block;
+ misc_block = new;
+ misc_block_index = 0;
+ total_free_miscs += MISC_BLOCK_SIZE;
}
- XSETMISC (val, &marker_block->markers[marker_block_index].m);
- marker_block_index++;
+ XSETMISC (val, &misc_block->miscs[misc_block_index].m);
+ misc_block_index++;
}
MALLOC_UNBLOCK_INPUT;
- --total_free_markers;
+ total_miscs++;
+ total_free_miscs--;
consing_since_gc += sizeof (union Lisp_Misc);
misc_objects_consed++;
XMISCANY (val)->gcmarkbit = 0;
@@ -3577,10 +3597,9 @@
free_misc (Lisp_Object misc)
{
XMISCTYPE (misc) = Lisp_Misc_Free;
- XMISC (misc)->u_free.chain = marker_free_list;
- marker_free_list = XMISC (misc);
-
- total_free_markers++;
+ XMISC (misc)->u_free.chain = misc_free_list;
+ misc_free_list = XMISC (misc);
+ total_free_miscs++;
}
/* Return a Lisp_Misc_Save_Value object containing POINTER and
@@ -4309,17 +4328,17 @@
{
if (m->type == MEM_TYPE_MISC)
{
- struct marker_block *b = (struct marker_block *) m->start;
- ptrdiff_t offset = (char *) p - (char *) &b->markers[0];
+ struct misc_block *b = (struct misc_block *) m->start;
+ ptrdiff_t offset = (char *) p - (char *) &b->miscs[0];
/* P must point to the start of a Lisp_Misc, not be
one of the unused cells in the current misc block,
and not be on the free-list. */
return (offset >= 0
- && offset % sizeof b->markers[0] == 0
- && offset < (MARKER_BLOCK_SIZE * sizeof b->markers[0])
- && (b != marker_block
- || offset / sizeof b->markers[0] < marker_block_index)
+ && offset % sizeof b->miscs[0] == 0
+ && offset < (MISC_BLOCK_SIZE * sizeof b->miscs[0])
+ && (b != misc_block
+ || offset / sizeof b->miscs[0] < misc_block_index)
&& ((union Lisp_Misc *) p)->u_any.type != Lisp_Misc_Free);
}
else
@@ -5345,10 +5364,14 @@
Garbage collection happens automatically if you cons more than
`gc-cons-threshold' bytes of Lisp data since previous garbage collection.
`garbage-collect' normally returns a list with info on amount of space in use:
- ((USED-CONSES . FREE-CONSES) (USED-SYMS . FREE-SYMS)
- (USED-MISCS . FREE-MISCS) USED-STRING-CHARS USED-VECTOR-SLOTS
- (USED-FLOATS . FREE-FLOATS) (USED-INTERVALS . FREE-INTERVALS)
- (USED-STRINGS . FREE-STRINGS))
+ ((CONS INTERNAL-SIZE USED-CONSES FREE-CONSES)
+ (SYMBOL INTERNAL-SIZE USED-SYMBOLS FREE-SYMBOLS)
+ (MISC INTERNAL-SIZE USED-MISCS FREE-MISCS)
+ (STRING INTERNAL-SIZE USED-STRINGS USED-STRING-BYTES FREE-STRING)
+ (VECTOR INTERNAL-SIZE USED-VECTORS USED-VECTOR-BYTES FREE-VECTOR-BYTES)
+ (FLOAT INTERNAL-SIZE USED-FLOATS FREE-FLOATS)
+ (INTERVAL INTERNAL-SIZE USED-INTERVALS FREE-INTERVALS)
+ (BUFFER INTERNAL-SIZE USED-BUFFERS))
However, if there was overflow in pure space, `garbage-collect'
returns nil, because real GC can't be done.
See Info node `(elisp)Garbage Collection'. */)
@@ -5594,9 +5617,9 @@
tot += total_conses * sizeof (struct Lisp_Cons);
tot += total_symbols * sizeof (struct Lisp_Symbol);
- tot += total_markers * sizeof (union Lisp_Misc);
- tot += total_string_size;
- tot += total_vector_size * sizeof (Lisp_Object);
+ tot += total_miscs * sizeof (union Lisp_Misc);
+ tot += total_string_bytes;
+ tot += total_vector_bytes;
tot += total_floats * sizeof (struct Lisp_Float);
tot += total_intervals * sizeof (struct interval);
tot += total_strings * sizeof (struct Lisp_String);
@@ -5621,20 +5644,38 @@
unbind_to (count, Qnil);
- total[0] = Fcons (make_number (total_conses),
+ total[0] = list4 (Qcons, make_number (sizeof (struct Lisp_Cons)),
+ make_number (total_conses),
make_number (total_free_conses));
- total[1] = Fcons (make_number (total_symbols),
+
+ total[1] = list4 (Qsymbol, make_number (sizeof (struct Lisp_Symbol)),
+ make_number (total_symbols),
make_number (total_free_symbols));
- total[2] = Fcons (make_number (total_markers),
- make_number (total_free_markers));
- total[3] = make_number (total_string_size);
- total[4] = make_number (total_vector_size);
- total[5] = Fcons (make_number (total_floats),
+
+ total[2] = list4 (Qmisc, make_number (sizeof (union Lisp_Misc)),
+ make_number (total_miscs),
+ make_number (total_free_miscs));
+
+ total[3] = list5 (Qstring, make_number (sizeof (struct Lisp_String)),
+ make_number (total_strings),
+ make_number (total_string_bytes),
+ make_number (total_free_strings));
+
+ total[4] = list5 (Qvector, make_number (sizeof (struct Lisp_Vector)),
+ make_number (total_vectors),
+ make_number (total_vector_bytes),
+ make_number (total_free_vector_bytes));
+
+ total[5] = list4 (Qfloat, make_number (sizeof (struct Lisp_Float)),
+ make_number (total_floats),
make_number (total_free_floats));
- total[6] = Fcons (make_number (total_intervals),
+
+ total[6] = list4 (Qinterval, make_number (sizeof (struct interval)),
+ make_number (total_intervals),
make_number (total_free_intervals));
- total[7] = Fcons (make_number (total_strings),
- make_number (total_free_strings));
+
+ total[7] = list3 (Qbuffer, make_number (sizeof (struct buffer)),
+ make_number (total_buffers));
#if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES
{
@@ -6477,47 +6518,47 @@
/* Put all unmarked misc's on free list.
For a marker, first unchain it from the buffer it points into. */
{
- register struct marker_block *mblk;
- struct marker_block **mprev = &marker_block;
- register int lim = marker_block_index;
+ register struct misc_block *mblk;
+ struct misc_block **mprev = &misc_block;
+ register int lim = misc_block_index;
EMACS_INT num_free = 0, num_used = 0;
- marker_free_list = 0;
+ misc_free_list = 0;
- for (mblk = marker_block; mblk; mblk = *mprev)
+ for (mblk = misc_block; mblk; mblk = *mprev)
{
register int i;
int this_free = 0;
for (i = 0; i < lim; i++)
{
- if (!mblk->markers[i].m.u_any.gcmarkbit)
+ if (!mblk->miscs[i].m.u_any.gcmarkbit)
{
- if (mblk->markers[i].m.u_any.type == Lisp_Misc_Marker)
- unchain_marker (&mblk->markers[i].m.u_marker);
+ if (mblk->miscs[i].m.u_any.type == Lisp_Misc_Marker)
+ unchain_marker (&mblk->miscs[i].m.u_marker);
/* Set the type of the freed object to Lisp_Misc_Free.
We could leave the type alone, since nobody checks it,
but this might catch bugs faster. */
- mblk->markers[i].m.u_marker.type = Lisp_Misc_Free;
- mblk->markers[i].m.u_free.chain = marker_free_list;
- marker_free_list = &mblk->markers[i].m;
+ mblk->miscs[i].m.u_marker.type = Lisp_Misc_Free;
+ mblk->miscs[i].m.u_free.chain = misc_free_list;
+ misc_free_list = &mblk->miscs[i].m;
this_free++;
}
else
{
num_used++;
- mblk->markers[i].m.u_any.gcmarkbit = 0;
+ mblk->miscs[i].m.u_any.gcmarkbit = 0;
}
}
- lim = MARKER_BLOCK_SIZE;
+ lim = MISC_BLOCK_SIZE;
/* If this block contains only free markers and we have already
seen more than two blocks worth of free markers then deallocate
this block. */
- if (this_free == MARKER_BLOCK_SIZE && num_free > MARKER_BLOCK_SIZE)
+ if (this_free == MISC_BLOCK_SIZE && num_free > MISC_BLOCK_SIZE)
{
*mprev = mblk->next;
/* Unhook from the free list. */
- marker_free_list = mblk->markers[0].m.u_free.chain;
+ misc_free_list = mblk->miscs[0].m.u_free.chain;
lisp_free (mblk);
}
else
@@ -6527,14 +6568,15 @@
}
}
- total_markers = num_used;
- total_free_markers = num_free;
+ total_miscs = num_used;
+ total_free_miscs = num_free;
}
/* Free all unmarked buffers */
{
register struct buffer *buffer = all_buffers, *prev = 0, *next;
+ total_buffers = 0;
while (buffer)
if (!VECTOR_MARKED_P (buffer))
{
@@ -6548,6 +6590,7 @@
}
else
{
+ total_buffers++;
VECTOR_UNMARK (buffer);
UNMARK_BALANCE_INTERVALS (BUF_INTERVALS (buffer));
prev = buffer, buffer = buffer->header.next.buffer;
@@ -6595,7 +6638,7 @@
data[0] = make_number
(min (MOST_POSITIVE_FIXNUM,
(total_free_conses * sizeof (struct Lisp_Cons)
- + total_free_markers * sizeof (union Lisp_Misc)
+ + total_free_miscs * sizeof (union Lisp_Misc)
+ total_free_symbols * sizeof (struct Lisp_Symbol)
+ total_free_floats * sizeof (struct Lisp_Float)
+ total_free_intervals * sizeof (struct interval)
@@ -6605,7 +6648,7 @@
data[1] = make_number
(min (MOST_POSITIVE_FIXNUM, mallinfo ().fordblks / 1024));
#else
- data[1] = make_number (0);
+ data[1] = Qnil;
#endif
return Flist (2, data);
}
=== modified file 'src/data.c'
--- src/data.c 2012-07-10 08:43:46 +0000
+++ src/data.c 2012-07-17 12:36:31 +0000
@@ -83,12 +83,12 @@
Lisp_Object Qfloatp;
Lisp_Object Qnumberp, Qnumber_or_marker_p;
-Lisp_Object Qinteger;
-static Lisp_Object Qsymbol, Qstring, Qcons, Qmarker, Qoverlay;
+Lisp_Object Qinteger, Qinterval, Qfloat, Qvector;
+Lisp_Object Qsymbol, Qstring, Qcons, Qmarker, Qmisc;
Lisp_Object Qwindow;
-static Lisp_Object Qfloat, Qwindow_configuration;
+static Lisp_Object Qoverlay, Qwindow_configuration;
static Lisp_Object Qprocess;
-static Lisp_Object Qcompiled_function, Qframe, Qvector;
+static Lisp_Object Qcompiled_function, Qframe;
Lisp_Object Qbuffer;
static Lisp_Object Qchar_table, Qbool_vector, Qhash_table;
static Lisp_Object Qsubrp, Qmany, Qunevalled;
@@ -3083,7 +3083,6 @@
DEFSYM (Qwindow_configuration, "window-configuration");
DEFSYM (Qprocess, "process");
DEFSYM (Qwindow, "window");
- /* DEFSYM (Qsubr, "subr"); */
DEFSYM (Qcompiled_function, "compiled-function");
DEFSYM (Qbuffer, "buffer");
DEFSYM (Qframe, "frame");
@@ -3091,6 +3090,9 @@
DEFSYM (Qchar_table, "char-table");
DEFSYM (Qbool_vector, "bool-vector");
DEFSYM (Qhash_table, "hash-table");
+ /* Used by Fgarbage_collect. */
+ DEFSYM (Qinterval, "interval");
+ DEFSYM (Qmisc, "misc");
DEFSYM (Qdefun, "defun");
=== modified file 'src/lisp.h'
--- src/lisp.h 2012-07-16 04:47:31 +0000
+++ src/lisp.h 2012-07-17 12:36:31 +0000
@@ -2332,7 +2332,8 @@
extern Lisp_Object Qfloatp;
extern Lisp_Object Qnumberp, Qnumber_or_marker_p;
-extern Lisp_Object Qinteger;
+extern Lisp_Object Qinteger, Qcons, Qsymbol, Qmarker, Qstring;
+extern Lisp_Object Qmisc, Qvector, Qfloat, Qinterval, Qbuffer;
extern Lisp_Object Qfont_spec, Qfont_entity, Qfont_object;
^ permalink raw reply [flat|nested] 2+ messages in thread