1 : // Copyright (c) 2011 The Native Client Authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 : //
5 :
6 : // Note: this file implements a simple replay engine for srpcs.
7 : // For simplicity this replay engine contains
8 : // some global data structures but since we only
9 : // expect to have a single NaClCommandLoop instance that
10 : // should not matter.
11 : #include "native_client/src/include/portability.h"
12 : #include "native_client/src/shared/srpc/nacl_srpc.h"
13 : #include "native_client/src/shared/platform/nacl_log.h"
14 : #include "native_client/src/trusted/sel_universal/parsing.h"
15 : #include "native_client/src/trusted/sel_universal/replay_handler.h"
16 : #include "native_client/src/trusted/sel_universal/rpc_universal.h"
17 :
18 :
19 : #include <stdio.h>
20 : #include <iterator>
21 : #include <algorithm>
22 : #include <string>
23 : #include <vector>
24 : #include <set>
25 :
26 : using std::string;
27 : using std::vector;
28 : using std::set;
29 :
30 : namespace {
31 :
32 :
33 0 : struct ReplayItem {
34 : int count;
35 : string signature;
36 : vector<string> args_in;
37 : vector<string> args_out;
38 : };
39 :
40 : // terminated if we cannot find a replay
41 : bool GlobalReplayStrict = true;
42 :
43 : // List of all "canned" rpcs, order is important
44 22 : vector<ReplayItem*> GlobalReplayList;
45 :
46 : // ugly hack to have access to the NaClCommandLoop even when processing an rpc
47 : NaClCommandLoop* GlobalCommandLoop;
48 :
49 : // Match a signature and a given set of input args against a replay time.
50 : // Return true if this item is a match that could be replayed
51 : bool RpcMatchesReplayItem(string signature,
52 : NaClSrpcArg** inputs,
53 0 : ReplayItem* ri) {
54 : UNREFERENCED_PARAMETER(inputs);
55 : // first check cound and signature
56 0 : if (ri->signature != signature) return false;
57 0 : if (ri->count == 0 ) return false;
58 :
59 0 : NaClLog(2, "found potential match for %s\n", signature.c_str());
60 :
61 : // now for the more costly parameter comparison
62 : // Build the input parameter values.
63 0 : const size_t n = ri->args_in.size();
64 : NaClSrpcArg in[NACL_SRPC_MAX_ARGS];
65 : NaClSrpcArg* inv[NACL_SRPC_MAX_ARGS + 1];
66 0 : BuildArgVec(inv, in, n);
67 0 : if (!ParseArgs(inv, ri->args_in, 0, true, GlobalCommandLoop)) {
68 : // TODO(sehr): reclaim memory here on failure.
69 0 : NaClLog(LOG_ERROR, "Bad input args for RPC.\n");
70 0 : return false;
71 : }
72 :
73 0 : const bool result = AllArgsEqual(inv, inputs);
74 0 : FreeArrayArgs(inv);
75 0 : return result;
76 : }
77 :
78 :
79 : void ReplayRpc(NaClSrpcRpc* rpc,
80 : NaClSrpcArg** inputs,
81 : NaClSrpcArg** outputs,
82 0 : NaClSrpcClosure* done) {
83 : // we just control from the nexe - clean the pipes
84 0 : fflush(stdout);
85 0 : fflush(stderr);
86 :
87 : const char* rpc_name;
88 : const char* arg_types;
89 : const char* ret_types;
90 :
91 0 : if (!NaClSrpcServiceMethodNameAndTypes(rpc->channel->server,
92 : rpc->rpc_number,
93 : &rpc_name,
94 : &arg_types,
95 : &ret_types)) {
96 0 : NaClLog(LOG_ERROR, "cannot find signature for rpc %d\n", rpc->rpc_number);
97 0 : rpc->result = NACL_SRPC_RESULT_APP_ERROR;
98 0 : done->Run(done);
99 0 : return;
100 : }
101 :
102 : NaClLog(1, "attempt to replay: %s (%s) -> %s\n",
103 0 : rpc_name, arg_types, ret_types);
104 :
105 0 : string signature = string(rpc_name) + ":" + arg_types + ":" + ret_types;
106 0 : for (size_t i = 0; i < GlobalReplayList.size(); ++i) {
107 0 : if (!RpcMatchesReplayItem(signature, inputs, GlobalReplayList[i])) {
108 0 : continue;
109 : }
110 0 : NaClLog(1, "found replay rpc\n");
111 0 : if (!ParseArgs(outputs,
112 : GlobalReplayList[i]->args_out,
113 : 0,
114 : true,
115 : GlobalCommandLoop)) {
116 0 : NaClLog(LOG_ERROR, "Bad input args for RPC.\n");
117 0 : break;
118 : }
119 :
120 0 : printf("replaying %s:\n", signature.c_str());
121 0 : GlobalCommandLoop->DumpArgsAndResults(inputs, outputs);
122 :
123 0 : rpc->result = NACL_SRPC_RESULT_OK;
124 0 : NaClLog(1, "invoke callback\n");
125 0 : --GlobalReplayList[i]->count;
126 0 : done->Run(done);
127 0 : return;
128 : }
129 :
130 0 : NaClLog(LOG_WARNING, "No replay rpc found for rpc %s, args:\n", rpc_name);
131 0 : for (size_t i = 0; inputs[i] != 0; ++i) {
132 0 : string value = DumpArg(inputs[i], GlobalCommandLoop);
133 0 : NaClLog(LOG_WARNING, "input %d: %s\n", (int) i, value.c_str());
134 : }
135 :
136 : // if we exit the for loop here we have an error
137 0 : rpc->result = NACL_SRPC_RESULT_APP_ERROR;
138 0 : done->Run(done);
139 :
140 0 : if (GlobalReplayStrict) {
141 0 : exit(-1);
142 0 : }
143 : }
144 : } // end namespace
145 :
146 :
147 : // Register a new replay item
148 0 : bool HandlerReplay(NaClCommandLoop* ncl, const vector<string>& args) {
149 : UNREFERENCED_PARAMETER(ncl);
150 : // we need three args at start and the "*" in out separator
151 0 : if (args.size() < 4) {
152 0 : NaClLog(LOG_ERROR, "Insufficient arguments to 'rpc' command.\n");
153 0 : return false;
154 : }
155 :
156 : // NOTE: small leak here
157 0 : ReplayItem* ri = new ReplayItem;
158 0 : ri->count = strtoul(args[1].c_str(), 0, 0);
159 0 : ri->signature = args[2];
160 :
161 : // TODO(robertm): use stl find
162 : size_t in_out_sep;
163 0 : for (in_out_sep = 3; in_out_sep < args.size(); ++in_out_sep) {
164 0 : if (args[in_out_sep] == "*")
165 0 : break;
166 : }
167 :
168 0 : if (in_out_sep == args.size()) {
169 0 : NaClLog(LOG_ERROR, "Missing input/output argument separator\n");
170 0 : return false;
171 : }
172 :
173 : copy(args.begin() + 3,
174 : args.begin() + in_out_sep,
175 0 : back_inserter(ri->args_in));
176 : copy(args.begin() + in_out_sep + 1,
177 : args.end(),
178 0 : back_inserter(ri->args_out));
179 :
180 0 : GlobalReplayList.push_back(ri);
181 0 : return true;
182 : }
183 :
184 : // Add srpc upcall for all replay signatures registered so far
185 0 : bool HandlerReplayActivate(NaClCommandLoop* ncl, const vector<string>& args) {
186 0 : if (args.size() != 1) {
187 0 : NaClLog(LOG_ERROR, "not the right number of args for this command\n");
188 0 : return false;
189 : }
190 :
191 0 : set<string> sigs;
192 0 : for (size_t i = 0; i < GlobalReplayList.size(); ++i) {
193 0 : sigs.insert(GlobalReplayList[i]->signature);
194 : }
195 :
196 0 : for (set<string>::iterator it = sigs.begin(); it != sigs.end(); ++it) {
197 0 : ncl->AddUpcallRpc(*it, ReplayRpc);
198 : }
199 : // ugly hack
200 0 : GlobalCommandLoop = ncl;
201 0 : return true;
202 : }
203 :
204 : // Report all unused replays - add the end of a tests there should be none
205 0 : bool HandlerUnusedReplays(NaClCommandLoop* ncl, const vector<string>& args) {
206 : UNREFERENCED_PARAMETER(ncl);
207 : UNREFERENCED_PARAMETER(args);
208 : // we need three args at start and the "*" in out separator
209 0 : for (size_t i = 0; i < GlobalReplayList.size(); ++i) {
210 0 : ReplayItem* ri = GlobalReplayList[i];
211 0 : if (ri->count == 0) continue;
212 :
213 0 : printf("%d %s\n", ri->count, ri->signature.c_str());
214 0 : for (size_t j = 0; j < ri->args_in.size(); ++j) {
215 0 : printf("in %d %s\n", static_cast<int>(j), ri->args_in[j].c_str());
216 : }
217 0 : for (size_t j = 0; j < ri->args_out.size(); ++j) {
218 0 : printf("out %d %s\n", static_cast<int>(j), ri->args_out[j].c_str());
219 : }
220 : }
221 0 : return true;
222 22 : }
|