mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 04:06:46 +00:00 
			
		
		
		
	 9ab4fb21f5
			
		
	
	
		9ab4fb21f5
		
	
	
	
	
		
			
			Properly free each command resp to avoid memory leak.
ASAN shows memory leak stack:
Indirect leak of 2352520 byte(s) in 571 object(s) allocated from:
    #0 0x7f6ca3308d4e in __interceptor_calloc (/lib64/libasan.so.5+0x112d4e)
    #1 0x7f6ca3127a50 in g_malloc0 (/lib64/libglib-2.0.so.0+0x55a50)
    #2 0x557bf3c71d2b in qdict_new ../qobject/qdict.c:29
    #3 0x557bf3c9caba in parse_object ../qobject/json-parser.c:318
    #4 0x557bf3c9ce75 in json_parser_parse ../qobject/json-parser.c:580
    #5 0x557bf3c8c8cf in json_message_process_token ../qobject/json-streamer.c:92
    #6 0x557bf3c9ea59 in json_lexer_feed_char ../qobject/json-lexer.c:313
    #7 0x557bf3c9eeb5 in json_lexer_feed ../qobject/json-lexer.c:350
    #8 0x557bf3c4793a in qmp_fd_receive ../tests/qtest/libqtest.c:608
    #9 0x557bf3c47b58 in qtest_qmp_receive ../tests/qtest/libqtest.c:618
    #10 0x557bf3c44245 in wait_command ../tests/qtest/migration-helpers.c:59
    #11 0x557bf3c445cb in migrate_query_status ../tests/qtest/migration-helpers.c:108
    #12 0x557bf3c44642 in check_migration_status ../tests/qtest/migration-helpers.c:124
    #13 0x557bf3c447e7 in wait_for_migration_status ../tests/qtest/migration-helpers.c:148
    #14 0x557bf3c43b8f in test_migrate_auto_converge ../tests/qtest/migration-test.c:1243
    ......
Fix: 5e34005571
Reported-by: Euler Robot <euler.robot@huawei.com>
Signed-off-by: Chen Qun <kuhn.chenqun@huawei.com>
Message-Id: <20201023061218.2080844-2-kuhn.chenqun@huawei.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
		
	
			
		
			
				
	
	
		
			191 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QTest migration helpers
 | |
|  *
 | |
|  * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
 | |
|  *   based on the vhost-user-test.c that is:
 | |
|  *      Copyright (c) 2014 Virtual Open Systems Sarl.
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | |
|  * See the COPYING file in the top-level directory.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qapi/qmp/qjson.h"
 | |
| 
 | |
| #include "migration-helpers.h"
 | |
| 
 | |
| bool got_stop;
 | |
| 
 | |
| static void check_stop_event(QTestState *who)
 | |
| {
 | |
|     QDict *event = qtest_qmp_event_ref(who, "STOP");
 | |
|     if (event) {
 | |
|         got_stop = true;
 | |
|         qobject_unref(event);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Events can get in the way of responses we are actually waiting for.
 | |
|  */
 | |
| QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
 | |
| {
 | |
|     va_list ap;
 | |
|     QDict *resp, *ret;
 | |
| 
 | |
|     va_start(ap, command);
 | |
|     qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
 | |
|     va_end(ap);
 | |
| 
 | |
|     resp = qtest_qmp_receive(who);
 | |
|     check_stop_event(who);
 | |
| 
 | |
|     g_assert(!qdict_haskey(resp, "error"));
 | |
|     g_assert(qdict_haskey(resp, "return"));
 | |
| 
 | |
|     ret = qdict_get_qdict(resp, "return");
 | |
|     qobject_ref(ret);
 | |
|     qobject_unref(resp);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Events can get in the way of responses we are actually waiting for.
 | |
|  */
 | |
| QDict *wait_command(QTestState *who, const char *command, ...)
 | |
| {
 | |
|     va_list ap;
 | |
|     QDict *resp, *ret;
 | |
| 
 | |
|     va_start(ap, command);
 | |
|     resp = qtest_vqmp(who, command, ap);
 | |
|     va_end(ap);
 | |
| 
 | |
|     check_stop_event(who);
 | |
| 
 | |
|     g_assert(!qdict_haskey(resp, "error"));
 | |
|     g_assert(qdict_haskey(resp, "return"));
 | |
| 
 | |
|     ret = qdict_get_qdict(resp, "return");
 | |
|     qobject_ref(ret);
 | |
|     qobject_unref(resp);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Send QMP command "migrate".
 | |
|  * Arguments are built from @fmt... (formatted like
 | |
|  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
 | |
|  */
 | |
| void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
 | |
| {
 | |
|     va_list ap;
 | |
|     QDict *args, *rsp;
 | |
| 
 | |
|     va_start(ap, fmt);
 | |
|     args = qdict_from_vjsonf_nofail(fmt, ap);
 | |
|     va_end(ap);
 | |
| 
 | |
|     g_assert(!qdict_haskey(args, "uri"));
 | |
|     qdict_put_str(args, "uri", uri);
 | |
| 
 | |
|     rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
 | |
| 
 | |
|     g_assert(qdict_haskey(rsp, "return"));
 | |
|     qobject_unref(rsp);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Note: caller is responsible to free the returned object via
 | |
|  * qobject_unref() after use
 | |
|  */
 | |
| QDict *migrate_query(QTestState *who)
 | |
| {
 | |
|     return wait_command(who, "{ 'execute': 'query-migrate' }");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Note: caller is responsible to free the returned object via
 | |
|  * g_free() after use
 | |
|  */
 | |
| static gchar *migrate_query_status(QTestState *who)
 | |
| {
 | |
|     QDict *rsp_return = migrate_query(who);
 | |
|     gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
 | |
| 
 | |
|     g_assert(status);
 | |
|     qobject_unref(rsp_return);
 | |
| 
 | |
|     return status;
 | |
| }
 | |
| 
 | |
| static bool check_migration_status(QTestState *who, const char *goal,
 | |
|                                    const char **ungoals)
 | |
| {
 | |
|     bool ready;
 | |
|     char *current_status;
 | |
|     const char **ungoal;
 | |
| 
 | |
|     current_status = migrate_query_status(who);
 | |
|     ready = strcmp(current_status, goal) == 0;
 | |
|     if (!ungoals) {
 | |
|         g_assert_cmpstr(current_status, !=, "failed");
 | |
|         /*
 | |
|          * If looking for a state other than completed,
 | |
|          * completion of migration would cause the test to
 | |
|          * hang.
 | |
|          */
 | |
|         if (strcmp(goal, "completed") != 0) {
 | |
|             g_assert_cmpstr(current_status, !=, "completed");
 | |
|         }
 | |
|     } else {
 | |
|         for (ungoal = ungoals; *ungoal; ungoal++) {
 | |
|             g_assert_cmpstr(current_status, !=,  *ungoal);
 | |
|         }
 | |
|     }
 | |
|     g_free(current_status);
 | |
|     return ready;
 | |
| }
 | |
| 
 | |
| void wait_for_migration_status(QTestState *who,
 | |
|                                const char *goal, const char **ungoals)
 | |
| {
 | |
|     while (!check_migration_status(who, goal, ungoals)) {
 | |
|         usleep(1000);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void wait_for_migration_complete(QTestState *who)
 | |
| {
 | |
|     wait_for_migration_status(who, "completed", NULL);
 | |
| }
 | |
| 
 | |
| void wait_for_migration_fail(QTestState *from, bool allow_active)
 | |
| {
 | |
|     QDict *rsp_return;
 | |
|     char *status;
 | |
|     bool failed;
 | |
| 
 | |
|     do {
 | |
|         status = migrate_query_status(from);
 | |
|         bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
 | |
|             (allow_active && !strcmp(status, "active"));
 | |
|         if (!result) {
 | |
|             fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
 | |
|                     __func__, status, allow_active);
 | |
|         }
 | |
|         g_assert(result);
 | |
|         failed = !strcmp(status, "failed");
 | |
|         g_free(status);
 | |
|     } while (!failed);
 | |
| 
 | |
|     /* Is the machine currently running? */
 | |
|     rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
 | |
|     g_assert(qdict_haskey(rsp_return, "running"));
 | |
|     g_assert(qdict_get_bool(rsp_return, "running"));
 | |
|     qobject_unref(rsp_return);
 | |
| }
 |