diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c
index 5abe1b23dd9bb610ebb605cba9aebb2ee8c3df7c..a286b2656c4d9c916afabc90aada838f099f66a1 100644
--- a/gtests/net/packetdrill/config.c
+++ b/gtests/net/packetdrill/config.c
@@ -57,6 +57,7 @@ enum option_codes {
 	OPT_WIRE_SERVER_DEV,
 	OPT_TCP_TS_TICK_USECS,
 	OPT_NON_FATAL,
+	OPT_DRY_RUN,
 	OPT_VERBOSE = 'v',	/* our only single-letter option */
 };
 
@@ -84,6 +85,7 @@ struct option options[] = {
 	{ "wire_server_dev",	.has_arg = true,  NULL, OPT_WIRE_SERVER_DEV },
 	{ "tcp_ts_tick_usecs",	.has_arg = true,  NULL, OPT_TCP_TS_TICK_USECS },
 	{ "non_fatal",		.has_arg = true,  NULL, OPT_NON_FATAL },
+	{ "dry_run",		.has_arg = false, NULL, OPT_DRY_RUN },
 	{ "verbose",		.has_arg = false, NULL, OPT_VERBOSE },
 	{ NULL },
 };
@@ -113,6 +115,7 @@ void show_usage(void)
 		"\t[--wire_server_port=<server_port>]\n"
 		"\t[--wire_client_dev=<eth_dev_name>]\n"
 		"\t[--wire_server_dev=<eth_dev_name>]\n"
+		"\t[--dry_run]\n"
 		"\t[--verbose|-v]\n"
 		"\tscript_path ...\n");
 }
@@ -443,6 +446,9 @@ static void process_option(int opt, char *optarg, struct config *config,
 	case OPT_WIRE_SERVER_DEV:
 		config->wire_server_device = strdup(optarg);
 		break;
+	case OPT_DRY_RUN:
+		config->dry_run = true;
+		break;
 	case OPT_VERBOSE:
 		config->verbose = true;
 		break;
diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h
index 795182853b95c7bbed9c2129ba502001c50c24b6..55190ce1167cc988b974f9e3ba9dca5d6f8412cc 100644
--- a/gtests/net/packetdrill/config.h
+++ b/gtests/net/packetdrill/config.h
@@ -78,6 +78,8 @@ struct config {
 	bool non_fatal_packet;		/* treat packet asserts as non-fatal */
 	bool non_fatal_syscall;		/* treat syscall asserts as non-fatal */
 
+	bool dry_run;			/* parse script but don't execute? */
+
 	bool verbose;			/* print detailed debug info? */
 	char *script_path;		/* pathname of script file */
 
@@ -101,7 +103,6 @@ struct config {
 	struct ip_address wire_server_ip;  /* IP of on-the-wire server */
 	char *wire_server_ip_string;	   /* malloc-ed server IP string */
 	u16 wire_server_port;		   /* the port the server listens on */
-
 };
 
 /* Top-level info about the invocation of a test script */
diff --git a/gtests/net/packetdrill/packetdrill.c b/gtests/net/packetdrill/packetdrill.c
index 5818872d7fd04b9466f5aef596548779813d6dfa..d95c6aaa3bc49211a6f1a21de245c6254f853904 100644
--- a/gtests/net/packetdrill/packetdrill.c
+++ b/gtests/net/packetdrill/packetdrill.c
@@ -101,6 +101,10 @@ int main(int argc, char *argv[])
 						script_path, NULL))
 			exit(EXIT_FAILURE);
 
+		/* If --dry_run, then don't actually execute the script. */
+		if (config.dry_run)
+			continue;
+
 		run_init_scripts(&config);
 		run_script(&config, &script);
 	}
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 6203a0ba853f89a11493c2324a884c439a33315f..a6d405f6462761804c3fcb9b0966f973bdd7c106 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -176,7 +176,7 @@ void read_script(const char *script_path, struct script *script)
 
 		/* Allocate a buffer big enough for the whole file. */
 		if (stat(script_path, &script_info) != 0)
-			die("stat() of script file '%s': %s\n",
+			die("parse error: stat() of script file '%s': %s\n",
 			    script_path, strerror(errno));
 
 		/* Pick a buffer size larger than the file, so we'll
@@ -190,12 +190,12 @@ void read_script(const char *script_path, struct script *script)
 		/* Read the file into our buffer. */
 		fd = open(script_path, O_RDONLY);
 		if (fd < 0)
-			die("error opening script file '%s': %s\n",
+			die("parse error opening script file '%s': %s\n",
 			    script_path, strerror(errno));
 
 		script->length = read(fd, script->buffer, size);
 		if (script->length < 0)
-			die("error reading script file '%s': %s\n",
+			die("parse error reading script file '%s': %s\n",
 			    script_path, strerror(errno));
 
 		/* If we filled the buffer, then probably another
@@ -239,7 +239,7 @@ int parse_script(const struct config *config,
 	/* Now parse the script from our buffer. */
 	yyin = fmemopen(script->buffer, script->length, "r");
 	if (yyin == NULL)
-		die_perror("fmemopen: error opening script buffer");
+		die_perror("fmemopen: parse error opening script buffer");
 
 	current_script_path = config->script_path;
 	in_config = config;
@@ -280,7 +280,7 @@ static void yyerror(const char *message)
 static void semantic_error(const char* message)
 {
 	assert(current_script_line >= 0);
-	die("%s:%d: error: %s\n",
+	die("%s:%d: semantic error: %s\n",
 	    current_script_path, current_script_line, message);
 }