1 |
|
%% Copyright (c) 2023 Peter Morgan <peter.james.morgan@gmail.com> |
2 |
|
%% |
3 |
|
%% Licensed under the Apache License, Version 2.0 (the "License"); |
4 |
|
%% you may not use this file except in compliance with the License. |
5 |
|
%% You may obtain a copy of the License at |
6 |
|
%% |
7 |
|
%% http://www.apache.org/licenses/LICENSE-2.0 |
8 |
|
%% |
9 |
|
%% Unless required by applicable law or agreed to in writing, software |
10 |
|
%% distributed under the License is distributed on an "AS IS" BASIS, |
11 |
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 |
|
%% See the License for the specific language governing permissions and |
13 |
|
%% limitations under the License. |
14 |
|
|
15 |
|
|
16 |
|
-module(msmp_debug). |
17 |
|
|
18 |
|
|
19 |
|
-feature(maybe_expr, enable). |
20 |
|
|
21 |
|
|
22 |
|
-export([binary_workflow/1]). |
23 |
|
-export([packetise/2]). |
24 |
|
-export([unhex/2]). |
25 |
|
-export([write_file/2]). |
26 |
|
-import(scran_branch, [alt/1]). |
27 |
|
-import(scran_bytes, [length_encoded/1]). |
28 |
|
-import(scran_combinator, [map_parser/2]). |
29 |
|
-import(scran_combinator, [map_result/2]). |
30 |
|
-import(scran_combinator, [peek/1]). |
31 |
|
-import(scran_combinator, [rest/0]). |
32 |
|
-import(scran_result, [into_map/1]). |
33 |
|
-import(scran_result, [kv/2]). |
34 |
|
-import(scran_sequence, [sequence/1]). |
35 |
|
-include_lib("kernel/include/logger.hrl"). |
36 |
|
|
37 |
|
|
38 |
|
dump(Parser) -> |
39 |
:-( |
fun |
40 |
|
(Input) -> |
41 |
:-( |
(into_map( |
42 |
|
map_parser( |
43 |
|
length_encoded( |
44 |
|
map_result( |
45 |
|
msmp_integer_fixed:decode(3), |
46 |
|
fun |
47 |
|
(Length) -> |
48 |
|
%% adjust length to include the sequence number |
49 |
:-( |
Length + 1 |
50 |
|
end)), |
51 |
|
sequence( |
52 |
|
[kv(encoded, |
53 |
|
map_result( |
54 |
|
peek(rest()), |
55 |
|
fun |
56 |
|
(<<Sequence:8, Body/bytes>>) -> |
57 |
:-( |
?LOG_DEBUG(#{size => byte_size(Body), |
58 |
|
sequence => Sequence, |
59 |
:-( |
packet => <<(byte_size(Body)):24/little, Sequence:8, Body/bytes>>}), |
60 |
:-( |
<<(byte_size(Body)):24/little, Sequence:8, Body/bytes>> |
61 |
|
end)), |
62 |
|
kv(sequence, msmp_integer_fixed:decode(1)), |
63 |
|
kv(packet, Parser)]))))(Input) |
64 |
|
end. |
65 |
|
|
66 |
|
parser(BinLog, ClientFlags) -> |
67 |
:-( |
fun |
68 |
|
(Input) -> |
69 |
:-( |
(alt([msmp_binlog_network_stream:decode(BinLog), |
70 |
|
msmp_binlog_dump:decode(), |
71 |
|
msmp_binlog_dump_gtid:decode(), |
72 |
|
msmp_handshake:decode(), |
73 |
|
msmp_handshake_response:decode(), |
74 |
|
msmp_packet_ok:decode(ClientFlags), |
75 |
|
msmp_packet_error:decode(ClientFlags), |
76 |
|
msmp_com_query:decode(ClientFlags), |
77 |
|
msmp_packet_eof:decode(ClientFlags), |
78 |
|
msmp_ssl_request:decode(), |
79 |
|
%% msmp_auth_more_data:decode(), |
80 |
|
msmp_register_replica:decode(), |
81 |
|
otherwise()]))(Input) |
82 |
|
end. |
83 |
|
|
84 |
|
otherwise() -> |
85 |
:-( |
fun |
86 |
|
(Input) -> |
87 |
:-( |
(into_map(sequence([kv(otherwise, rest())])))(Input) |
88 |
|
end. |
89 |
|
|
90 |
|
|
91 |
|
repeated(BinLog, ClientFlags) -> |
92 |
:-( |
fun |
93 |
|
(Input) -> |
94 |
:-( |
(?FUNCTION_NAME(BinLog, ClientFlags, []))(Input) |
95 |
|
end. |
96 |
|
|
97 |
|
|
98 |
|
repeated(#{mapped := Mapped} = BinLog, ClientFlags, A) -> |
99 |
:-( |
fun |
100 |
|
(Input) -> |
101 |
|
%% ?LOG_DEBUG(#{input => Input, a_length => length(A)}), |
102 |
|
|
103 |
:-( |
case (dump(parser(BinLog, ClientFlags)))(Input) of |
104 |
|
{Remaining, |
105 |
|
#{packet := #{action := handshake, |
106 |
|
capability_flags_1 := Flags1, |
107 |
|
capability_flags_2 := Flags2}} = Packet} -> |
108 |
:-( |
?LOG_DEBUG(#{packet => Packet}), |
109 |
:-( |
(?FUNCTION_NAME( |
110 |
|
BinLog, |
111 |
|
maps:merge(Flags1, Flags2), |
112 |
|
[Packet | A])) |
113 |
|
(Remaining); |
114 |
|
|
115 |
|
{Remaining, |
116 |
|
#{packet := #{action := handshake_response, |
117 |
|
client_flags := Flags}} = Packet} -> |
118 |
:-( |
?LOG_DEBUG(#{packet => Packet}), |
119 |
:-( |
(?FUNCTION_NAME( |
120 |
|
BinLog, |
121 |
|
Flags, |
122 |
|
[Packet | A])) |
123 |
|
(Remaining); |
124 |
|
|
125 |
|
{Remaining, |
126 |
|
#{packet := #{action := log_event, |
127 |
|
header := #{event_type := table_map}, |
128 |
|
event := #{table_id := TableId} = Event}} = Packet} -> |
129 |
:-( |
?LOG_DEBUG(#{packet => Packet}), |
130 |
:-( |
(?FUNCTION_NAME( |
131 |
|
BinLog#{mapped := Mapped#{TableId => maps:without([table_id], Event)}}, |
132 |
|
ClientFlags, |
133 |
|
[Packet | A])) |
134 |
|
(Remaining); |
135 |
|
|
136 |
|
{Remaining, #{} = Packet} -> |
137 |
:-( |
?LOG_DEBUG(#{packet => Packet}), |
138 |
:-( |
(?FUNCTION_NAME( |
139 |
|
BinLog, |
140 |
|
ClientFlags, |
141 |
|
[Packet | A])) |
142 |
|
(Remaining); |
143 |
|
|
144 |
|
nomatch when A == [] -> |
145 |
:-( |
nomatch; |
146 |
|
|
147 |
|
nomatch -> |
148 |
:-( |
{Input, lists:reverse(A)} |
149 |
|
end |
150 |
|
end. |
151 |
|
|
152 |
|
|
153 |
|
write_file(In, Out) -> |
154 |
:-( |
{ok, Packets} = file:consult(In), |
155 |
|
|
156 |
:-( |
ClientFlags = #{protocol_41 => true, |
157 |
|
session_track => true, |
158 |
|
transactions => true}, |
159 |
|
|
160 |
:-( |
BinLog = #{mapped => #{}}, |
161 |
|
|
162 |
:-( |
{<<>>, Decoded} = (repeated( |
163 |
|
BinLog, |
164 |
|
ClientFlags)) |
165 |
|
(iolist_to_binary( |
166 |
:-( |
[Data || {frame, Data} <- Packets])), |
167 |
:-( |
{ok, Header} = file:read_file("HEADER.terms"), |
168 |
:-( |
ok = write_terms(Out, Header, Decoded). |
169 |
|
|
170 |
|
|
171 |
|
write_terms(Filename, Header, Terms) -> |
172 |
:-( |
file:write_file( |
173 |
|
Filename, |
174 |
|
[Header, |
175 |
|
"\n", |
176 |
|
lists:map( |
177 |
|
fun |
178 |
|
(Term) -> |
179 |
:-( |
io_lib:format("~p.~n~n", [Term]) |
180 |
|
end, |
181 |
|
Terms)]). |
182 |
|
|
183 |
|
|
184 |
|
packetise(In, Out) -> |
185 |
:-( |
{ok, Packets} = file:consult(In), |
186 |
:-( |
{ok, Header} = file:read_file("HEADER.terms"), |
187 |
|
|
188 |
:-( |
write_terms( |
189 |
|
Out, |
190 |
|
Header, |
191 |
|
lists:reverse( |
192 |
|
frames(iolist_to_binary( |
193 |
|
lists:filtermap( |
194 |
|
fun |
195 |
|
(#{packet := #{ip := #{tcp := #{data := Data}}}}) when Data /= <<>> -> |
196 |
:-( |
{true, Data}; |
197 |
|
|
198 |
|
(_) -> |
199 |
:-( |
false |
200 |
|
end, |
201 |
|
Packets))))). |
202 |
|
|
203 |
|
|
204 |
|
unhex(In, Out) -> |
205 |
:-( |
{ok, Hexed} = file:read_file(In), |
206 |
:-( |
{ok, Header} = file:read_file("HEADER.terms"), |
207 |
:-( |
write_terms( |
208 |
|
Out, |
209 |
|
Header, |
210 |
|
lists:reverse( |
211 |
|
frames( |
212 |
|
iolist_to_binary( |
213 |
:-( |
[binary:decode_hex(Hex) || Hex <- binary:split(Hexed, <<"\n">>, [global,trim_all])])))). |
214 |
|
|
215 |
|
binary_workflow(In) -> |
216 |
:-( |
ok = unhex(In ++ ".binary", In ++ ".terms"), |
217 |
:-( |
ok = write_file(In ++ ".terms", In ++ "-msmp-debug.terms"). |
218 |
|
|
219 |
|
|
220 |
|
frames(Frames) -> |
221 |
:-( |
?FUNCTION_NAME(Frames, []). |
222 |
|
|
223 |
|
frames(<<Length:24/little, SeqBody:(Length + 1)/bytes, Remainder/bytes>>, A) -> |
224 |
:-( |
?FUNCTION_NAME(Remainder, |
225 |
|
[{frame, <<Length:24/little, SeqBody:(Length + 1)/bytes>>} | A]); |
226 |
|
frames(<<>>, A) -> |
227 |
:-( |
A; |
228 |
|
|
229 |
|
frames(Remainder, A) -> |
230 |
:-( |
[{remaining, Remainder} | A]. |