refactor: free more reachable memory with EXITFREE (#26349)

Discovered using __sanitizer_print_memory_profile().
This commit is contained in:
zeertzjq 2023-12-02 07:55:44 +08:00 committed by GitHub
parent 983defd284
commit f6e5366d00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 151 additions and 18 deletions

View File

@ -106,22 +106,37 @@ static void mpack_str(char **buf, const char *str)
*buf += len;
}
static void remote_ui_destroy(UI *ui)
FUNC_ATTR_NONNULL_ALL
{
UIData *data = ui->data;
kv_destroy(data->call_buf);
XFREE_CLEAR(ui->term_name);
xfree(ui);
}
void remote_ui_disconnect(uint64_t channel_id)
{
UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
if (!ui) {
return;
}
UIData *data = ui->data;
kv_destroy(data->call_buf);
pmap_del(uint64_t)(&connected_uis, channel_id, NULL);
ui_detach_impl(ui, channel_id);
// Destroy `ui`.
XFREE_CLEAR(ui->term_name);
xfree(ui);
remote_ui_destroy(ui);
}
#ifdef EXITFREE
void remote_ui_free_all_mem(void)
{
UI *ui;
map_foreach_value(&connected_uis, ui, {
remote_ui_destroy(ui);
});
map_destroy(uint64_t, &connected_uis);
}
#endif
/// Wait until ui has connected on stdio channel if only_stdio
/// is true, otherwise any channel.
void remote_ui_wait_for_attach(bool only_stdio)

View File

@ -2573,6 +2573,11 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
{
static bool recursive = false;
#ifdef EXITFREE
if (entered_free_all_mem) {
return;
}
#endif
if (starting == NO_SCREEN) {
return; // user config hasn't been sourced yet
}

View File

@ -53,13 +53,25 @@ static uint64_t next_chan_id = CHAN_STDERR + 1;
/// Teardown the module
void channel_teardown(void)
{
Channel *channel;
map_foreach_value(&channels, channel, {
channel_close(channel->id, kChannelPartAll, NULL);
Channel *chan;
map_foreach_value(&channels, chan, {
channel_close(chan->id, kChannelPartAll, NULL);
});
}
#ifdef EXITFREE
void channel_free_all_mem(void)
{
Channel *chan;
map_foreach_value(&channels, chan, {
channel_destroy(chan);
});
map_destroy(uint64_t, &channels);
callback_free(&on_print);
}
#endif
/// Closes a channel
///
/// @param id The channel id
@ -260,9 +272,8 @@ void callback_reader_start(CallbackReader *reader, const char *type)
reader->type = type;
}
static void free_channel_event(void **argv)
static void channel_destroy(Channel *chan)
{
Channel *chan = argv[0];
if (chan->is_rpc) {
rpc_free(chan);
}
@ -275,11 +286,17 @@ static void free_channel_event(void **argv)
callback_reader_free(&chan->on_stderr);
callback_free(&chan->on_exit);
pmap_del(uint64_t)(&channels, chan->id, NULL);
multiqueue_free(chan->events);
xfree(chan);
}
static void free_channel_event(void **argv)
{
Channel *chan = argv[0];
pmap_del(uint64_t)(&channels, chan->id, NULL);
channel_destroy(chan);
}
static void channel_destroy_early(Channel *chan)
{
if ((chan->id != --next_chan_id)) {

View File

@ -500,6 +500,10 @@ static void evalvars_clear(void)
p->vv_list = NULL;
}
}
partial_unref(vvlua_partial);
vimvars[VV_LUA].vv_partial = vvlua_partial = NULL;
hash_clear(&vimvarht);
hash_init(&vimvarht); // garbage_collect() will access it
hash_clear(&compat_hashtab);

View File

@ -879,15 +879,20 @@ void grid_free(ScreenGrid *grid)
grid->line_offset = NULL;
}
#ifdef EXITFREE
/// Doesn't allow reinit, so must only be called by free_all_mem!
void grid_free_all_mem(void)
{
grid_free(&default_grid);
grid_free(&msg_grid);
XFREE_CLEAR(msg_grid.dirty_col);
xfree(linebuf_char);
xfree(linebuf_attr);
xfree(linebuf_vcol);
xfree(linebuf_scratch);
set_destroy(glyph, &glyph_cache);
}
#endif
/// (Re)allocates a window grid if size changed while in ext_multigrid mode.
/// Updates size, offsets and handle for the grid regardless.

View File

@ -172,9 +172,14 @@ MAP_DECLS(ColorKey, ColorItem)
#define set_put_ref(T, set, key, key_alloc) set_put_##T(set, key, key_alloc)
#define set_put_idx(T, set, key, status) mh_put_##T(set, key, status)
#define set_del(T, set, key) set_del_##T(set, key)
#define set_destroy(T, set) (xfree((set)->keys), xfree((set)->h.hash))
#define set_clear(T, set) mh_clear(&(set)->h)
#define set_size(set) ((set)->h.size)
#define set_clear(T, set) mh_clear(&(set)->h)
#define set_destroy(T, set) \
do { \
xfree((set)->keys); \
xfree((set)->h.hash); \
*(set) = (Set(T)) SET_INIT; \
} while (0)
#define map_get(T, U) map_get_##T##U
#define map_has(T, map, key) set_has(T, &(map)->set, key)
@ -182,9 +187,13 @@ MAP_DECLS(ColorKey, ColorItem)
#define map_ref(T, U) map_ref_##T##U
#define map_put_ref(T, U) map_put_ref_##T##U
#define map_del(T, U) map_del_##T##U
#define map_destroy(T, map) (set_destroy(T, &(map)->set), xfree((map)->values))
#define map_clear(T, map) set_clear(T, &(map)->set)
#define map_size(map) set_size(&(map)->set)
#define map_clear(T, map) set_clear(T, &(map)->set)
#define map_destroy(T, map) \
do { \
set_destroy(T, &(map)->set); \
XFREE_CLEAR((map)->values); \
} while (0)
#define pmap_get(T) map_get(T, ptr_t)
#define pmap_put(T) map_put(T, ptr_t)

View File

@ -1117,7 +1117,6 @@ void marktree_clear(MarkTree *b)
b->root = NULL;
}
map_destroy(uint64_t, b->id2node);
*b->id2node = (PMap(uint64_t)) MAP_INIT;
b->n_keys = 0;
assert(b->n_nodes == 0);
}

View File

@ -9,9 +9,12 @@
#include <time.h>
#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
#include "nvim/arglist.h"
#include "nvim/ascii_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/channel.h"
#include "nvim/context.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawline.h"
@ -23,14 +26,19 @@
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
#include "nvim/mapping.h"
#include "nvim/memfile.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
#include "nvim/os/input.h"
#include "nvim/sign.h"
#include "nvim/state_defs.h"
#include "nvim/statusline.h"
#include "nvim/ui.h"
#include "nvim/ui_client.h"
#include "nvim/ui_compositor.h"
#include "nvim/usercmd.h"
#include "nvim/vim_defs.h"
@ -670,6 +678,7 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/grid.h"
# include "nvim/mark.h"
# include "nvim/msgpack_rpc/channel.h"
# include "nvim/msgpack_rpc/helpers.h"
# include "nvim/ops.h"
# include "nvim/option.h"
# include "nvim/os/os.h"
@ -738,6 +747,7 @@ void free_all_mem(void)
free_all_marks();
alist_clear(&global_alist);
free_homedir();
free_envmap();
free_users();
free_search_patterns();
free_old_sub();
@ -792,6 +802,7 @@ void free_all_mem(void)
}
}
channel_free_all_mem();
eval_clear();
api_extmark_free_all_mem();
ctx_free_all();
@ -815,8 +826,14 @@ void free_all_mem(void)
buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
}
map_destroy(int, &buffer_handles);
map_destroy(int, &window_handles);
map_destroy(int, &tabpage_handles);
// free screenlines (can't display anything now!)
grid_free_all_mem();
stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
xfree(tab_page_click_defs);
clear_hl_tables(false);
@ -824,10 +841,18 @@ void free_all_mem(void)
decor_free_all_mem();
drawline_free_all_mem();
input_free_all_mem();
if (ui_client_channel_id) {
ui_client_free_all_mem();
}
remote_ui_free_all_mem();
ui_free_all_mem();
ui_comp_free_all_mem();
nlua_free_all_mem();
rpc_free_all_mem();
msgpack_rpc_helpers_free_all_mem();
// should be last, in case earlier free functions deallocates arenas
arena_free_reuse_blks();

View File

@ -751,6 +751,7 @@ const char *get_client_info(Channel *chan, const char *key)
return NULL;
}
#ifdef EXITFREE
void rpc_free_all_mem(void)
{
cstr_t key;
@ -758,4 +759,8 @@ void rpc_free_all_mem(void)
xfree((void *)key);
});
set_destroy(cstr_t, &event_strings);
msgpack_sbuffer_destroy(&out_buffer);
multiqueue_free(ch_before_blocking_events);
}
#endif

View File

@ -27,6 +27,14 @@ void msgpack_rpc_helpers_init(void)
msgpack_sbuffer_init(&sbuffer);
}
#ifdef EXITFREE
void msgpack_rpc_helpers_free_all_mem(void)
{
msgpack_zone_destroy(&zone);
msgpack_sbuffer_destroy(&sbuffer);
}
#endif
typedef struct {
const msgpack_object *mobj;
Object *aobj;

View File

@ -583,6 +583,9 @@ void free_all_options(void)
}
free_operatorfunc_option();
free_tagfunc_option();
XFREE_CLEAR(fenc_default);
XFREE_CLEAR(p_term);
XFREE_CLEAR(p_ttytype);
}
#endif

View File

@ -510,6 +510,17 @@ void free_homedir(void)
xfree(homedir);
}
void free_envmap(void)
{
cstr_t name;
ptr_t e;
map_foreach(&envmap, name, e, {
xfree((char *)name);
xfree(e);
});
map_destroy(cstr_t, &envmap);
}
#endif
/// Call expand_env() and store the result in an allocated string.

View File

@ -74,6 +74,13 @@ void input_stop(void)
stream_close(&read_stream, NULL, NULL);
}
#ifdef EXITFREE
void input_free_all_mem(void)
{
rbuffer_free(input_buffer);
}
#endif
static void cursorhold_event(void **argv)
{
event_T event = State & MODE_INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD;

View File

@ -134,6 +134,8 @@ void ui_free_all_mem(void)
free_ui_event_callback(event_cb);
})
map_destroy(uint32_t, &ui_event_cbs);
multiqueue_free(resize_events);
}
#endif

View File

@ -210,3 +210,12 @@ void ui_client_event_raw_line(GridLineEvent *g)
tui_raw_line(tui, grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags,
(const schar_T *)grid_line_buf_char, grid_line_buf_attr);
}
#ifdef EXITFREE
void ui_client_free_all_mem(void)
{
tui_free_all_mem(tui);
xfree(grid_line_buf_char);
xfree(grid_line_buf_attr);
}
#endif

View File

@ -60,6 +60,15 @@ void ui_comp_init(void)
curgrid = &default_grid;
}
#ifdef EXITFREE
void ui_comp_free_all_mem(void)
{
kv_destroy(layers);
xfree(linebuf);
xfree(attrbuf);
}
#endif
void ui_comp_syn_init(void)
{
dbghl_normal = syn_check_group(S_LEN("RedrawDebugNormal"));