| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Net::Traces::SSFNet; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
26244
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
40
|
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
25
|
|
|
5
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
3350
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Net::Traces::SSFNet - Analyze traces generated by SSFNet |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Net::Traces::SSFNet qw( droptail_record_player droptail_record_plotter ); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
$Net::Traces::SSFNet::PRINT_EXACT_DECIMAL_DIGITS = 0; |
|
16
|
|
|
|
|
|
|
$Net::Traces::SSFNet::SHOW_SOURCES = 1; |
|
17
|
|
|
|
|
|
|
$Net::Traces::SSFNet::SHOW_STATS = 0; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# Use with traces created by either |
|
20
|
|
|
|
|
|
|
# SSF.Net.droptailQueueMonitor_1 or SSF.Net.droptailQueueMonitor_2 |
|
21
|
|
|
|
|
|
|
# |
|
22
|
|
|
|
|
|
|
droptail_record_player('q.trace', 'text.output', 'some_stream_id.0'); |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Use with traces created by SSF.Net.droptailQueueMonitor_1 |
|
25
|
|
|
|
|
|
|
# |
|
26
|
|
|
|
|
|
|
droptail_record_plotter('q.trace', 'some_stream_id.0', 'drops', 'pkts', 'av_qlen'); |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# Use with traces created by SSF.Net.droptailQueueMonitor_2 |
|
29
|
|
|
|
|
|
|
# |
|
30
|
|
|
|
|
|
|
droptail_record_plotter('q.trace', 'some_stream_id.0', 'drops', 'pkts', 'sumpkts', 'sumdrops'); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=cut |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
require Exporter; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
our @ISA = qw( Exporter ); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
our @EXPORT = qw( ); |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
41
|
|
|
|
|
|
|
droptail_assert_input |
|
42
|
|
|
|
|
|
|
droptail_assert_output |
|
43
|
|
|
|
|
|
|
droptail_record_player |
|
44
|
|
|
|
|
|
|
droptail_record_plotter |
|
45
|
|
|
|
|
|
|
); |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my %supported_record_types = |
|
50
|
|
|
|
|
|
|
( |
|
51
|
|
|
|
|
|
|
'SSF.Net.QueueRecord_1' => 'SSF.Net.droptailQueueMonitor_1', |
|
52
|
|
|
|
|
|
|
'SSF.Net.QueueProbeIntRecord' => 'SSF.Net.droptailQueueMonitor_1', |
|
53
|
|
|
|
|
|
|
'SSF.Net.QueueRecord_2' => 'SSF.Net.droptailQueueMonitor_2', |
|
54
|
|
|
|
|
|
|
); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 ABSTRACT |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Net::Traces::SSFNet can analyze traces created by L
|
|
59
|
|
|
|
|
|
|
Framework Network Models|"SEE ALSO">. It efficiently emulates in Perl |
|
60
|
|
|
|
|
|
|
the functionality provided by Java-based, SSFNet-bundled trace |
|
61
|
|
|
|
|
|
|
analyzers, and adds new features, including allowing for finer |
|
62
|
|
|
|
|
|
|
granularity in the processed output. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 INSTALLATION |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
See perlmodinstall. |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
SSF, the Scalable Simulation Framework, is a public-domain standard |
|
71
|
|
|
|
|
|
|
for discrete-event simulation of large, complex systems in Java and |
|
72
|
|
|
|
|
|
|
C++. SSFNet is a collection of models used for simulating |
|
73
|
|
|
|
|
|
|
telecommunication networks. The main goal of this module to ease the |
|
74
|
|
|
|
|
|
|
analysis of traces produced by L. |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Net::Traces::SSFNet version 0.02 can analyze traces generated by |
|
77
|
|
|
|
|
|
|
SSF.Net.droptailQueueMonitor_1 and SSF.Net.droptailQueueMonitor_2. |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head2 Analyzing SSF.Net.droptailQueueMonitor traces |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Net::Traces::SSFNet can analyze traces created by |
|
82
|
|
|
|
|
|
|
SSF.Net.droptailQueueMonitor_1 and SSF.Net.droptailQueueMonitor_2, |
|
83
|
|
|
|
|
|
|
effectively L the functionality |
|
84
|
|
|
|
|
|
|
of SSF.Net.droptailRecordPlayer_1 and SSF.Net.droptailRecordPlayer_2. |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
To replicate the functionality of either |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
java SSF.Net.droptailRecordPlayer_1 qlog.0 some_stream_id.0 |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
or |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
java SSF.Net.droptailRecordPlayer_2 qlog.0 some_stream_id.0 |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
use the following code: |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
use Net::Traces::SSFNet qw( droptail_record_player ); |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
$Net::Traces::SSFNet::PRINT_EXACT_DECIMAL_DIGITS = 0; |
|
100
|
|
|
|
|
|
|
droptail_record_player( 'qlog.0', *STDOUT, 'some_stream_id.0'); |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Notice that you do not have to specify what kind of records are |
|
103
|
|
|
|
|
|
|
contained in the trace. In fact, a trace may contain records created |
|
104
|
|
|
|
|
|
|
from both SSF.Net.droptailQueueMonitor's. |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 Finer granularity |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Although both SSF.Net.droptailQueueMonitor's capture simulation events |
|
109
|
|
|
|
|
|
|
using 64-bit Cs, the SSFNet-bundled trace processing utilities |
|
110
|
|
|
|
|
|
|
(SSF.Net.droptailRecordPlayer_1 and SSF.Net.droptailRecordPlayer_2) |
|
111
|
|
|
|
|
|
|
use 3 decimal digits when generating the processed |
|
112
|
|
|
|
|
|
|
output. Consequently, the text output is limited to millisecond |
|
113
|
|
|
|
|
|
|
granularity. |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
This can be an issue when events occur in sub-millisecond intervals: |
|
116
|
|
|
|
|
|
|
the original SSFNet record players do not carry this information in |
|
117
|
|
|
|
|
|
|
the text output. The following example might make the issue more |
|
118
|
|
|
|
|
|
|
clear. Remember that each node in a network graph is uniquely |
|
119
|
|
|
|
|
|
|
identified via a I (NHI) string (see |
|
120
|
|
|
|
|
|
|
http://www.ssfnet.org/InternetDocs/ssfnetDMLReference.html#addresses). Suppose |
|
121
|
|
|
|
|
|
|
that NHI 4(0) is sampled every 0.1 ms and NHI 4(1) every 1 ms. When |
|
122
|
|
|
|
|
|
|
SSF.Net.droptailRecordPlayer_2 processes the trace file, it will |
|
123
|
|
|
|
|
|
|
generate something like this |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
50.805 4(0) sumpkts 186 sumdrops 0 pkts 0 drops 0 |
|
126
|
|
|
|
|
|
|
50.805 4(0) sumpkts 187 sumdrops 0 pkts 1 drops 0 |
|
127
|
|
|
|
|
|
|
50.805 4(0) sumpkts 187 sumdrops 0 pkts 0 drops 0 |
|
128
|
|
|
|
|
|
|
50.805 4(0) sumpkts 187 sumdrops 0 pkts 0 drops 0 |
|
129
|
|
|
|
|
|
|
50.806 4(0) sumpkts 188 sumdrops 0 pkts 1 drops 0 |
|
130
|
|
|
|
|
|
|
50.806 4(0) sumpkts 188 sumdrops 0 pkts 0 drops 0 |
|
131
|
|
|
|
|
|
|
50.806 4(0) sumpkts 189 sumdrops 0 pkts 1 drops 0 |
|
132
|
|
|
|
|
|
|
50.806 4(0) sumpkts 189 sumdrops 0 pkts 0 drops 0 |
|
133
|
|
|
|
|
|
|
50.806 4(0) sumpkts 190 sumdrops 0 pkts 1 drops 0 |
|
134
|
|
|
|
|
|
|
50.806 4(1) sumpkts 51 sumdrops 0 pkts 0 drops 0 |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
while L will |
|
137
|
|
|
|
|
|
|
generate |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
50.8051 4(0) sumpkts 186 sumdrops 0 pkts 0 drops 0 |
|
140
|
|
|
|
|
|
|
50.8052 4(0) sumpkts 187 sumdrops 0 pkts 1 drops 0 |
|
141
|
|
|
|
|
|
|
50.8053 4(0) sumpkts 187 sumdrops 0 pkts 0 drops 0 |
|
142
|
|
|
|
|
|
|
50.8054 4(0) sumpkts 187 sumdrops 0 pkts 0 drops 0 |
|
143
|
|
|
|
|
|
|
50.8055 4(0) sumpkts 188 sumdrops 0 pkts 1 drops 0 |
|
144
|
|
|
|
|
|
|
50.8056 4(0) sumpkts 188 sumdrops 0 pkts 0 drops 0 |
|
145
|
|
|
|
|
|
|
50.8057 4(0) sumpkts 189 sumdrops 0 pkts 1 drops 0 |
|
146
|
|
|
|
|
|
|
50.8058 4(0) sumpkts 189 sumdrops 0 pkts 0 drops 0 |
|
147
|
|
|
|
|
|
|
50.8059 4(0) sumpkts 190 sumdrops 0 pkts 1 drops 0 |
|
148
|
|
|
|
|
|
|
50.806 4(1) sumpkts 51 sumdrops 0 pkts 0 drops 0 |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
provided that L<"$PRINT_EXACT_DECIMAL_DIGITS"> is set. |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head2 Improved Performance |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
L processes a queue |
|
155
|
|
|
|
|
|
|
trace generated by either SSF.Net.droptailQueueMonitor_1 or |
|
156
|
|
|
|
|
|
|
SSF.Net.droptailQueueMonitor_2 significantly faster that |
|
157
|
|
|
|
|
|
|
SSF.Net.droptailRecordPlayer_1 or SSF.Net.droptailRecordPlayer_2, |
|
158
|
|
|
|
|
|
|
respectively. |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head2 Additional functionality |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Use L to process |
|
163
|
|
|
|
|
|
|
a queue trace and generate text files ready for plotting using |
|
164
|
|
|
|
|
|
|
I, I, or even a spreadsheet application. |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head1 VARIABLES |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Net::Traces::SSFNet uses the following variables to control |
|
169
|
|
|
|
|
|
|
information generation. None is exported. |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head2 $PRINT_EXACT_DECIMAL_DIGITS |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
This variable is set by default in order to achieve L
|
|
174
|
|
|
|
|
|
|
granularity| "Finer granularity"> in the processed output. If you want |
|
175
|
|
|
|
|
|
|
to mimic the behavior of SSF.Net.droptailRecordPlayer_1 and |
|
176
|
|
|
|
|
|
|
SSF.Net.droptailRecordPlayer_2 use |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
$Net::Traces::SSFNet::PRINT_EXACT_DECIMAL_DIGITS = 0; |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 $SHOW_SOURCES |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
If $SHOW_SOURCES is set, droptail_record_player() and |
|
183
|
|
|
|
|
|
|
droptail_record_plotter() print to STDERR the types of records and |
|
184
|
|
|
|
|
|
|
traffic sources (NHI) found in the trace. For example, you may see |
|
185
|
|
|
|
|
|
|
something like this: |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Trace contains records from NHI 4(2) |
|
188
|
|
|
|
|
|
|
Trace contains records of type "SSF.Net.QueueRecord_2" |
|
189
|
|
|
|
|
|
|
Trace contains records from NHI 4(1) |
|
190
|
|
|
|
|
|
|
Trace contains records from NHI 4(0) |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
By default, no such information is sent to STDERR. |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 $SHOW_STATS |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
If $SHOW_STATS is set, droptail_record_player() and |
|
197
|
|
|
|
|
|
|
droptail_record_plotter() display trace processing statistics on |
|
198
|
|
|
|
|
|
|
STDERR. For example, you may see something like this: |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
{Player processed 113776 records, 1820393 bytes in 7.05 seconds (252 KB/s)} |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
This variable is set by default. If you want to suppress displaying |
|
203
|
|
|
|
|
|
|
the statistics use |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
$Net::Traces::SSFNet::SHOW_STATS = 0; |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
our $PRINT_EXACT_DECIMAL_DIGITS = 1; |
|
210
|
|
|
|
|
|
|
our $SHOW_SOURCES = 0; |
|
211
|
|
|
|
|
|
|
our $SHOW_STATS = 1; |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 droptail_assert_input |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
droptail_assert_input LIST |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
This function asserts that the input FILEHANDLE is valid and open |
|
220
|
|
|
|
|
|
|
before the real trace processing begins processing. LIST is expected |
|
221
|
|
|
|
|
|
|
to have up to two elements: IN and STREAM_ID. The queue trace IN may |
|
222
|
|
|
|
|
|
|
be either an open FILEHANDLE or a I. STREAM_ID, if |
|
223
|
|
|
|
|
|
|
specified, must match the stream ID encoded in the queue trace file. |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
droptail_assert_input() verifies that IN is open for reading and |
|
226
|
|
|
|
|
|
|
includes a valid preamble. If IN is not specified, it defaults to |
|
227
|
|
|
|
|
|
|
STDIN. |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
droptail_assert_input() returns a list containing the input |
|
230
|
|
|
|
|
|
|
FILEHANDLE, and the actual stream ID found in the queue trace file. |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=cut |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub droptail_assert_input { |
|
235
|
|
|
|
|
|
|
|
|
236
|
3
|
|
|
3
|
1
|
12
|
my ( $in, $stream_id ) = @_; |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# The following makes sure that $in is an open FILEHANDLE: If $in |
|
239
|
|
|
|
|
|
|
# was not provided, use STDIN. If $in is a filename, attempt to open |
|
240
|
|
|
|
|
|
|
# it for input. Otherwise, use $in as-is. |
|
241
|
|
|
|
|
|
|
# |
|
242
|
3
|
50
|
|
|
|
27
|
if ( not defined $in ) { |
|
|
|
50
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
0
|
$in = \*STDIN; |
|
244
|
0
|
0
|
|
|
|
0
|
carp 'No input FILEHANDLE or filename provided, using STDIN' |
|
245
|
|
|
|
|
|
|
if wantarray; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
elsif ( not defined fileno $in ) { |
|
248
|
3
|
50
|
|
|
|
148
|
open(IN_FH, '<', $in) |
|
249
|
|
|
|
|
|
|
or croak "Cannot open $in ($!)"; |
|
250
|
|
|
|
|
|
|
|
|
251
|
3
|
|
|
|
|
6
|
binmode IN_FH; # Needed for Windows; no harm on Unix |
|
252
|
|
|
|
|
|
|
|
|
253
|
3
|
|
|
|
|
8
|
$in = \*IN_FH; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# Each valid queue trace file starts with a preamble. This preamble |
|
257
|
|
|
|
|
|
|
# is actually inserted by SSF.Util.Streams, upon which both |
|
258
|
|
|
|
|
|
|
# SSF.Net.droptailQueueMonitor_1 and SSF.Net.droptailQueueMonitor_2 |
|
259
|
|
|
|
|
|
|
# are based. Below we assert that this preamble indeed exists, |
|
260
|
|
|
|
|
|
|
# before continuing to process the rest of the trace. |
|
261
|
|
|
|
|
|
|
# |
|
262
|
|
|
|
|
|
|
# Note that SSF.Util.Streams assumes that only a single stream ID is |
|
263
|
|
|
|
|
|
|
# present in a given trace. |
|
264
|
|
|
|
|
|
|
# |
|
265
|
3
|
|
|
|
|
9
|
my $utf_string = readUTF($in); |
|
266
|
|
|
|
|
|
|
|
|
267
|
3
|
50
|
|
|
|
9
|
croak "Bad header command: \"$utf_string\" (expected \"record\")" |
|
268
|
|
|
|
|
|
|
unless $utf_string eq 'record'; |
|
269
|
|
|
|
|
|
|
|
|
270
|
3
|
|
|
|
|
7
|
$utf_string = readUTF($in); |
|
271
|
|
|
|
|
|
|
|
|
272
|
3
|
100
|
|
|
|
9
|
if ( defined $stream_id ) { |
|
273
|
2
|
50
|
|
|
|
8
|
croak "Stream ID mismatch \"$utf_string\" (expected \"$stream_id\")" |
|
274
|
|
|
|
|
|
|
unless $utf_string eq $stream_id; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
else { |
|
277
|
1
|
|
|
|
|
3
|
$stream_id = $utf_string; |
|
278
|
1
|
50
|
|
|
|
4
|
carp |
|
279
|
|
|
|
|
|
|
"No stream ID provided; trace contains stream ID: \"", |
|
280
|
|
|
|
|
|
|
$utf_string, "\"" if wantarray ; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
|
|
283
|
3
|
|
|
|
|
16
|
return ( $in, $stream_id ); |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
} # End assert_input() |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head2 droptail_assert_output |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
droptail_assert_output FILEHANDLE |
|
290
|
|
|
|
|
|
|
droptail_assert_output filename |
|
291
|
|
|
|
|
|
|
droptail_assert_output |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
This function returns a valid and open output FILEHANDLE. If |
|
294
|
|
|
|
|
|
|
FILEHANDLE is open, it is returned as-is. If a I is provided |
|
295
|
|
|
|
|
|
|
instead, this function attempts to open and return a filehandle to it. |
|
296
|
|
|
|
|
|
|
If neither a FILEHANDLE nor a I is provided, the returned |
|
297
|
|
|
|
|
|
|
FILEHANDLE defaults to STDOUT. |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=cut |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub droptail_assert_output { |
|
302
|
|
|
|
|
|
|
|
|
303
|
1
|
|
|
1
|
1
|
3
|
my $out = shift; |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# If no FILEHANDLE or filename is provided, use STDOUT. |
|
306
|
|
|
|
|
|
|
# |
|
307
|
1
|
50
|
|
|
|
11
|
if ( not defined $out ) { |
|
|
|
50
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
$out = \*STDOUT; |
|
309
|
0
|
0
|
|
|
|
0
|
carp 'No output FILEHANDLE or filename provided, using STDOUT' |
|
310
|
|
|
|
|
|
|
if defined wantarray; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
elsif ( not defined fileno $out ) { |
|
313
|
1
|
50
|
|
|
|
46
|
open(OUT_FH, '>', $out) |
|
314
|
|
|
|
|
|
|
or croak "Cannot open $out ($!)"; |
|
315
|
|
|
|
|
|
|
|
|
316
|
1
|
|
|
|
|
4
|
$out = \*OUT_FH; |
|
317
|
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
|
|
319
|
1
|
|
|
|
|
3
|
return $out; |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
} # End assert_output() |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=head2 droptail_record_player |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
droptail_record_player LIST |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
This function processes binary traces generated by |
|
329
|
|
|
|
|
|
|
SSF.Net.droptailQueueMonitor_1 and SSF.Net.droptailQueueMonitor_2, |
|
330
|
|
|
|
|
|
|
generates text output based on the contents of the binary trace, and |
|
331
|
|
|
|
|
|
|
returns the number of records processed. In addition to seamlessly |
|
332
|
|
|
|
|
|
|
emulating the functionality of SSF.Net.droptailRecordPlayer_1 and |
|
333
|
|
|
|
|
|
|
SSF.Net.droptailRecordPlayer_2, droptail_record_player() can deal with |
|
334
|
|
|
|
|
|
|
traces that contain a mix of records from both types of |
|
335
|
|
|
|
|
|
|
droptailQueueMonitor's. |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
LIST is expected to have up to three elements: IN, OUT, and |
|
338
|
|
|
|
|
|
|
STREAM_ID. IN and OUT may be either an open FILEHANDLE or a |
|
339
|
|
|
|
|
|
|
I. STREAM_ID, if specified, must match the stream ID |
|
340
|
|
|
|
|
|
|
encoded in the queue trace file. LIST is asserted via |
|
341
|
|
|
|
|
|
|
L and |
|
342
|
|
|
|
|
|
|
L. |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
A record created from SSF.Net.droptailQueueMonitor_1 will generate a |
|
345
|
|
|
|
|
|
|
line like this in OUT |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
6.01 4(1) pkts 7 drops 0 av_qlen 5.73492479324341 |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
where "6.01" is the simulation time when the queue at NHI "4(1)" was |
|
350
|
|
|
|
|
|
|
sampled. Since the last time the queue was sampled, 7 packets were |
|
351
|
|
|
|
|
|
|
enqueued, 0 were dropped, and the average number of bytes buffered |
|
352
|
|
|
|
|
|
|
during this interval was 5.73492479324341. |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Similarly, a record created from SSF.Net.droptailQueueMonitor_2 will |
|
355
|
|
|
|
|
|
|
generate a line like this |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
99.1 4(2) sumpkts 55 sumdrops 0 pkts 0 drops 0 |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
where "99.1" is the simulation time when the queue at NHI "4(2)" was |
|
360
|
|
|
|
|
|
|
sampled. Since the beginning of the simulation, this interface has |
|
361
|
|
|
|
|
|
|
enqueued a total of 55 packets and has dropped none. Since the last |
|
362
|
|
|
|
|
|
|
time the queue was sampled, 0 packets were enqueued and 0 were |
|
363
|
|
|
|
|
|
|
dropped. |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=cut |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub droptail_record_player { |
|
368
|
|
|
|
|
|
|
|
|
369
|
1
|
|
|
1
|
1
|
3
|
my ( $in_fh, $out_fh, $stream_id ) = @_; |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# Assert input and output FILEHANLDEs |
|
372
|
|
|
|
|
|
|
# |
|
373
|
1
|
|
|
|
|
5
|
( $in_fh, $stream_id ) = droptail_assert_input( $in_fh, $stream_id ); |
|
374
|
|
|
|
|
|
|
|
|
375
|
1
|
|
|
|
|
4
|
$out_fh = droptail_assert_output( $out_fh ); |
|
376
|
|
|
|
|
|
|
|
|
377
|
1
|
|
|
|
|
2
|
my %types; # of trace records |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
my %sources; # in the trace (interfaces - NHIs) |
|
380
|
|
|
|
|
|
|
|
|
381
|
0
|
|
|
|
|
0
|
my %times; # current simulation time for a given source |
|
382
|
|
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
0
|
my %time_decimals; # number of decimal digits used when printing the |
|
384
|
|
|
|
|
|
|
# simulation times for each source |
|
385
|
|
|
|
|
|
|
|
|
386
|
0
|
|
|
|
|
0
|
my %sampling_intervals; # for each source |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# Used for gathering statistics to measure processing performance: |
|
389
|
|
|
|
|
|
|
# Number of $records and $bytes processed in $seconds |
|
390
|
|
|
|
|
|
|
# |
|
391
|
1
|
|
|
|
|
22
|
my ( $records, $bytes, $seconds ) = ( 0, 0, times ); |
|
392
|
|
|
|
|
|
|
|
|
393
|
1
|
|
|
|
|
2
|
my $trace_record; |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# Each record starts with a 20-byte "header-like" part containing |
|
396
|
|
|
|
|
|
|
# the record $type, a unique $stream identifier, the simulation |
|
397
|
|
|
|
|
|
|
# $time in seconds, and the number of bytes ($length) left to be |
|
398
|
|
|
|
|
|
|
# read in the current record. |
|
399
|
|
|
|
|
|
|
# |
|
400
|
1
|
|
|
|
|
2
|
my ( $type, $stream, $time, $length ); |
|
401
|
|
|
|
|
|
|
|
|
402
|
1
|
|
|
|
|
8
|
while( read($in_fh, $trace_record, 20) ) { |
|
403
|
|
|
|
|
|
|
|
|
404
|
9
|
|
|
|
|
33
|
( $type, $stream, $time, $length ) = unpack("N N B64 N", $trace_record); |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# A $type code of 0 indicates that the current record |
|
407
|
|
|
|
|
|
|
# defines/contains a type code. See %supported_record_types above. |
|
408
|
|
|
|
|
|
|
# |
|
409
|
9
|
100
|
|
|
|
25
|
$type == 0 && do { |
|
410
|
|
|
|
|
|
|
|
|
411
|
3
|
|
|
|
|
4
|
$records++; |
|
412
|
|
|
|
|
|
|
|
|
413
|
3
|
|
|
|
|
7
|
read( $in_fh, $trace_record, $length ); |
|
414
|
3
|
|
|
|
|
4
|
$bytes += $length; |
|
415
|
|
|
|
|
|
|
|
|
416
|
3
|
|
|
|
|
32
|
my ( $t_id, $t_name ) = split ' ', $trace_record; |
|
417
|
|
|
|
|
|
|
|
|
418
|
3
|
50
|
|
|
|
12
|
croak "Unsupported record type \"$t_name\"\n" |
|
419
|
|
|
|
|
|
|
unless $supported_record_types{$t_name}; |
|
420
|
|
|
|
|
|
|
|
|
421
|
3
|
50
|
|
|
|
7
|
warn "Trace contains records of type \"$t_name\"\n" if $SHOW_SOURCES; |
|
422
|
|
|
|
|
|
|
|
|
423
|
3
|
|
|
|
|
6
|
$types{$t_id} = $t_name; |
|
424
|
|
|
|
|
|
|
|
|
425
|
3
|
|
|
|
|
10
|
next; |
|
426
|
|
|
|
|
|
|
}; |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# A type code of 1 indicates that the current record contains a |
|
429
|
|
|
|
|
|
|
# stream id and the stream name, which is the NHI corresponding to |
|
430
|
|
|
|
|
|
|
# the interface being monitored. |
|
431
|
|
|
|
|
|
|
# |
|
432
|
6
|
100
|
|
|
|
15
|
$type == 1 && do { |
|
433
|
|
|
|
|
|
|
|
|
434
|
3
|
|
|
|
|
4
|
$records++; |
|
435
|
|
|
|
|
|
|
|
|
436
|
3
|
|
|
|
|
6
|
read($in_fh, $trace_record, $length); |
|
437
|
3
|
|
|
|
|
4
|
$bytes += $length; |
|
438
|
|
|
|
|
|
|
|
|
439
|
3
|
|
|
|
|
9
|
my ( $s_id, $s_name ) = split ' ', $trace_record; |
|
440
|
3
|
50
|
|
|
|
10
|
warn "Trace contains records from NHI $s_name\n" if $SHOW_SOURCES; |
|
441
|
|
|
|
|
|
|
|
|
442
|
3
|
|
|
|
|
9
|
$sources{$s_id} = $s_name; |
|
443
|
|
|
|
|
|
|
|
|
444
|
3
|
|
|
|
|
4
|
$times{$s_id} = 0; |
|
445
|
3
|
|
|
|
|
7
|
$time_decimals{$s_id} = '%.3f'; |
|
446
|
|
|
|
|
|
|
|
|
447
|
3
|
|
|
|
|
11
|
next; |
|
448
|
|
|
|
|
|
|
}; |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# The following type name (SSF.Net.QueueProbeIntRecord) and |
|
451
|
|
|
|
|
|
|
# associated $type code is used by SSF.Net.droptailQueueMonitor_1 |
|
452
|
|
|
|
|
|
|
# to store the sampling interval. SSF.Net.droptailQueueMonitor_1 |
|
453
|
|
|
|
|
|
|
# samples the queue using this interval, but stores a record only |
|
454
|
|
|
|
|
|
|
# when there is a change in the queue. On the other hand, |
|
455
|
|
|
|
|
|
|
# SSF.Net.droptailQueueMonitor_2 stores a record regardless of |
|
456
|
|
|
|
|
|
|
# whether any packets were enqueued since the last sample was |
|
457
|
|
|
|
|
|
|
# made. |
|
458
|
|
|
|
|
|
|
# |
|
459
|
3
|
100
|
|
|
|
9
|
$types{$type} eq 'SSF.Net.QueueProbeIntRecord' && do { |
|
460
|
|
|
|
|
|
|
|
|
461
|
2
|
|
|
|
|
4
|
$records++; |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# From the next 8 bytes only the first 4 are useful: A float |
|
464
|
|
|
|
|
|
|
# carrying the interval used by SSF.Net.droptailQueueMonitor_1 |
|
465
|
|
|
|
|
|
|
# to sample the queue size. This is due to a hack in the |
|
466
|
|
|
|
|
|
|
# original Java code. |
|
467
|
|
|
|
|
|
|
# |
|
468
|
2
|
|
|
|
|
4
|
read($in_fh, $trace_record, 8); |
|
469
|
2
|
|
|
|
|
3
|
$bytes += 8; |
|
470
|
|
|
|
|
|
|
|
|
471
|
2
|
|
|
|
|
9
|
$sampling_intervals{$stream} = |
|
472
|
|
|
|
|
|
|
int_bits_to_float( unpack("V", $trace_record) ); |
|
473
|
|
|
|
|
|
|
|
|
474
|
2
|
|
|
|
|
3
|
print { $out_fh } |
|
|
2
|
|
|
|
|
8
|
|
|
475
|
|
|
|
|
|
|
sprintf($time_decimals{$stream}, long_bits_to_double($time)), |
|
476
|
|
|
|
|
|
|
" $sources{$stream} probe_interval $sampling_intervals{$stream}\n"; |
|
477
|
|
|
|
|
|
|
|
|
478
|
2
|
50
|
|
|
|
8
|
if ( $PRINT_EXACT_DECIMAL_DIGITS ) { |
|
479
|
|
|
|
|
|
|
# Extract the exact sampling interval from the trace, and |
|
480
|
|
|
|
|
|
|
# present the actual time in the generated output |
|
481
|
|
|
|
|
|
|
# |
|
482
|
2
|
|
|
|
|
16
|
my $d = log( $sampling_intervals{$stream} ) / log(10); |
|
483
|
2
|
50
|
|
|
|
7
|
$d = $d < 0 ? sprintf("%.0f",-$d) : 0; |
|
484
|
|
|
|
|
|
|
|
|
485
|
2
|
|
|
|
|
9
|
$time_decimals{$stream} =~ s/3/$d/; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
2
|
|
|
|
|
8
|
next; |
|
489
|
|
|
|
|
|
|
}; |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# This must be the first actual queue record. We're past the trace |
|
492
|
|
|
|
|
|
|
# preamble, so exit this loop. |
|
493
|
|
|
|
|
|
|
# |
|
494
|
1
|
|
|
|
|
2
|
last; |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
|
|
497
|
1
|
|
|
|
|
2
|
do {{ |
|
498
|
|
|
|
|
|
|
|
|
499
|
13
|
|
|
|
|
13
|
( $type, $stream, $time, $length ) = unpack("N N B64 N", $trace_record); |
|
|
13
|
|
|
|
|
40
|
|
|
500
|
|
|
|
|
|
|
|
|
501
|
13
|
|
|
|
|
17
|
$records++; |
|
502
|
|
|
|
|
|
|
|
|
503
|
13
|
100
|
|
|
|
30
|
$types{$type} eq 'SSF.Net.QueueRecord_1' && do { |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# Read the next 12 bytes, which correspond to one float |
|
506
|
|
|
|
|
|
|
# carrying the average queue length in bytes (over the |
|
507
|
|
|
|
|
|
|
# sampling interval), and two 32-bit integers: |
|
508
|
|
|
|
|
|
|
# |
|
509
|
|
|
|
|
|
|
# * the number of packets enqueued during the current sampling |
|
510
|
|
|
|
|
|
|
# interval ($pkts) |
|
511
|
|
|
|
|
|
|
# |
|
512
|
|
|
|
|
|
|
# * the number of dropped packets during the current sampling |
|
513
|
|
|
|
|
|
|
# interval ($drops) |
|
514
|
|
|
|
|
|
|
# |
|
515
|
3
|
|
|
|
|
6
|
read($in_fh, $trace_record, 12); |
|
516
|
|
|
|
|
|
|
|
|
517
|
3
|
|
|
|
|
3
|
$bytes += 12; |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# You may notice that SSF.OS.NetFlow.BytesUtil uses a custom way |
|
520
|
|
|
|
|
|
|
# for storing 32-bit integers. Integers are actually stored in |
|
521
|
|
|
|
|
|
|
# little-endian binary format, contrary to standard Java, which |
|
522
|
|
|
|
|
|
|
# is big-endian. |
|
523
|
|
|
|
|
|
|
# |
|
524
|
3
|
|
|
|
|
6
|
my ($q_length, $pkts, $drops) = unpack("V V V", $trace_record); |
|
525
|
|
|
|
|
|
|
|
|
526
|
3
|
|
|
|
|
4
|
print { $out_fh } |
|
|
3
|
|
|
|
|
7
|
|
|
527
|
|
|
|
|
|
|
sprintf($time_decimals{$stream}, long_bits_to_double($time)), |
|
528
|
|
|
|
|
|
|
" $sources{$stream} pkts $pkts drops $drops av_qlen ", |
|
529
|
|
|
|
|
|
|
int_bits_to_float($q_length), "\n"; |
|
530
|
|
|
|
|
|
|
|
|
531
|
3
|
|
|
|
|
16
|
next; |
|
532
|
|
|
|
|
|
|
}; |
|
533
|
|
|
|
|
|
|
|
|
534
|
10
|
50
|
|
|
|
23
|
$types{$type} eq 'SSF.Net.QueueRecord_2' && do { |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# First make sure that we got the sampling interval for this |
|
537
|
|
|
|
|
|
|
# $stream |
|
538
|
|
|
|
|
|
|
# |
|
539
|
10
|
100
|
|
|
|
24
|
if ( not defined $sampling_intervals{$stream} ) { |
|
540
|
1
|
|
|
|
|
3
|
$sampling_intervals{$stream} = long_bits_to_double($time); |
|
541
|
|
|
|
|
|
|
|
|
542
|
1
|
50
|
|
|
|
3
|
if ( $PRINT_EXACT_DECIMAL_DIGITS ) { |
|
543
|
|
|
|
|
|
|
# Extract the exact sampling interval from the trace, and |
|
544
|
|
|
|
|
|
|
# present the actual time in the generated output |
|
545
|
|
|
|
|
|
|
# |
|
546
|
1
|
|
|
|
|
4
|
my $d = log( $sampling_intervals{$stream} ) / log(10); |
|
547
|
1
|
50
|
|
|
|
3
|
$d = $d < 0 ? sprintf("%.0f",-$d) : 0; |
|
548
|
|
|
|
|
|
|
|
|
549
|
1
|
|
|
|
|
4
|
$time_decimals{$stream} =~ s/3/$d/; |
|
550
|
|
|
|
|
|
|
} |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
|
|
553
|
10
|
|
|
|
|
24
|
$times{$stream} += $sampling_intervals{$stream}; |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# Read the next 16 bytes, which correspond to four 32-bit |
|
556
|
|
|
|
|
|
|
# integers: |
|
557
|
|
|
|
|
|
|
# |
|
558
|
|
|
|
|
|
|
# * the total number of packets enqueued at the interface from |
|
559
|
|
|
|
|
|
|
# the beginning of the simulation ($sumpkts) |
|
560
|
|
|
|
|
|
|
# |
|
561
|
|
|
|
|
|
|
# * the total number of packets dropped at the interface from |
|
562
|
|
|
|
|
|
|
# the beginning of the simulation ($sumdrops) |
|
563
|
|
|
|
|
|
|
# |
|
564
|
|
|
|
|
|
|
# * the number of packets enqueued during the current sampling |
|
565
|
|
|
|
|
|
|
# interval ($pkts) |
|
566
|
|
|
|
|
|
|
# |
|
567
|
|
|
|
|
|
|
# * the number of dropped packets during the current sampling |
|
568
|
|
|
|
|
|
|
# interval ($drops) |
|
569
|
|
|
|
|
|
|
# |
|
570
|
10
|
|
|
|
|
14
|
read($in_fh, $trace_record, 16); |
|
571
|
|
|
|
|
|
|
|
|
572
|
10
|
|
|
|
|
11
|
$bytes += 16; |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# SSF.OS.NetFlow.BytesUtil uses a custom way for storing 32-bit |
|
575
|
|
|
|
|
|
|
# integers. Integers are actually stored in little-endian binary |
|
576
|
|
|
|
|
|
|
# format, contrary to standard Java, which is big-endian. |
|
577
|
|
|
|
|
|
|
# |
|
578
|
10
|
|
|
|
|
24
|
my ( $sumpkts, $sumdrops, $pkts, $drops ) = |
|
579
|
|
|
|
|
|
|
unpack("V V V V", $trace_record); |
|
580
|
|
|
|
|
|
|
|
|
581
|
10
|
|
|
|
|
12
|
print { $out_fh } |
|
|
10
|
|
|
|
|
55
|
|
|
582
|
|
|
|
|
|
|
sprintf($time_decimals{$stream}, $times{$stream}), |
|
583
|
|
|
|
|
|
|
" $sources{$stream} sumpkts $sumpkts sumdrops $sumdrops pkts ", |
|
584
|
|
|
|
|
|
|
$pkts, " drops $drops\n"; |
|
585
|
|
|
|
|
|
|
|
|
586
|
10
|
|
|
|
|
30
|
next; |
|
587
|
|
|
|
|
|
|
}; |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
}} while( read($in_fh, $trace_record, 20) ); |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
# Display processing stats |
|
592
|
|
|
|
|
|
|
# |
|
593
|
1
|
50
|
|
|
|
3
|
if ( $SHOW_STATS ) { |
|
594
|
0
|
|
|
|
|
0
|
$seconds = times - $seconds ; |
|
595
|
0
|
|
|
|
|
0
|
my $rate = 'Inf'; |
|
596
|
0
|
0
|
|
|
|
0
|
if ( $seconds > 0 ) { |
|
597
|
0
|
|
|
|
|
0
|
$rate = sprintf("%.0f", $bytes / (1024 * $seconds)); |
|
598
|
|
|
|
|
|
|
} |
|
599
|
|
|
|
|
|
|
warn |
|
600
|
0
|
|
|
|
|
0
|
"{Player processed $records records, ", |
|
601
|
|
|
|
|
|
|
$bytes, " bytes in $seconds seconds ($rate KB/s)}\n"; |
|
602
|
|
|
|
|
|
|
} |
|
603
|
|
|
|
|
|
|
|
|
604
|
1
|
|
|
|
|
8
|
return $records; |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
} # end of droptail_record_player() |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=head2 droptail_record_plotter |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
droptail_record_plotter LIST |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
This function processes binary traces generated by |
|
614
|
|
|
|
|
|
|
SSF.Net.droptailQueueMonitor_1 and SSF.Net.droptailQueueMonitor_2, and |
|
615
|
|
|
|
|
|
|
generates text files suitable for plotting using, for example, |
|
616
|
|
|
|
|
|
|
I. It returns the number of records processed. |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
LIST should start with an open FILEHANDLE or a I, followed |
|
619
|
|
|
|
|
|
|
by a STREAM_ID, which must match the stream ID encoded in the queue |
|
620
|
|
|
|
|
|
|
trace file. This part of the LIST is asserted via |
|
621
|
|
|
|
|
|
|
L. |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
Following these two elements droptail_record_plotter() expects to see |
|
624
|
|
|
|
|
|
|
at least one of the following strings: 'pkts, 'drops', 'sumpkts', |
|
625
|
|
|
|
|
|
|
'sumdrops', and 'av_qlen'. For each of these strings and each source |
|
626
|
|
|
|
|
|
|
NHI found in the trace, droptail_record_plotter() creates a text file |
|
627
|
|
|
|
|
|
|
in the I. For example, if a trace file |
|
628
|
|
|
|
|
|
|
includes records from two NHIs, 2(0) and 2(1), the following call |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
droptail_record_plotter( 'qlog.0', 'some_stream_id.0', 'drops', 'pkts'); |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
will create 4 files: "2(0).pkts", "2(0).drops", "2(1).pkts", and |
|
633
|
|
|
|
|
|
|
"2(1).drops". Each of these files has two columns: the first one is |
|
634
|
|
|
|
|
|
|
the simulation time; the second is the value of the respective metric. |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Notice that you do not have to specify what kind of records are |
|
637
|
|
|
|
|
|
|
contained in the trace. In fact, a trace may contain records created |
|
638
|
|
|
|
|
|
|
from both SSF.Net.droptailQueueMonitor's. |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=cut |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
sub droptail_record_plotter { |
|
643
|
|
|
|
|
|
|
|
|
644
|
0
|
|
|
0
|
1
|
0
|
my ( $in_fh, $stream_id ) = droptail_assert_input( shift, shift ); |
|
645
|
|
|
|
|
|
|
|
|
646
|
0
|
|
|
|
|
0
|
my %can_plot = ( |
|
647
|
|
|
|
|
|
|
sumpkts => 0, |
|
648
|
|
|
|
|
|
|
sumdrops => 0, |
|
649
|
|
|
|
|
|
|
pkts => 0, |
|
650
|
|
|
|
|
|
|
drops => 0, |
|
651
|
|
|
|
|
|
|
av_qlen => 0, |
|
652
|
|
|
|
|
|
|
); |
|
653
|
|
|
|
|
|
|
|
|
654
|
0
|
|
|
|
|
0
|
my ( %plot, %plot_fh ); |
|
655
|
|
|
|
|
|
|
|
|
656
|
0
|
|
|
|
|
0
|
foreach my $p ( @_ ) { |
|
657
|
0
|
0
|
|
|
|
0
|
if ( defined $can_plot{$p} ) { |
|
658
|
0
|
|
|
|
|
0
|
$plot{$p} = $p; |
|
659
|
|
|
|
|
|
|
} |
|
660
|
|
|
|
|
|
|
else { |
|
661
|
0
|
|
|
|
|
0
|
croak "Do not know how to plot \"$p\""; |
|
662
|
|
|
|
|
|
|
} |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
|
|
665
|
0
|
|
|
|
|
0
|
my %types; # of trace records |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
my %sources; # in the trace (interfaces - NHI's) |
|
668
|
|
|
|
|
|
|
|
|
669
|
0
|
|
|
|
|
0
|
my %times; # current simulation time for a given source |
|
670
|
|
|
|
|
|
|
|
|
671
|
0
|
|
|
|
|
0
|
my %time_decimals; # number of decimal digits used when printing the |
|
672
|
|
|
|
|
|
|
# simulation times for each source |
|
673
|
|
|
|
|
|
|
|
|
674
|
0
|
|
|
|
|
0
|
my %sampling_intervals; # for each source |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# Used for gathering statistics to measure processing performance: |
|
677
|
|
|
|
|
|
|
# Number of $records and $bytes processed in $seconds |
|
678
|
|
|
|
|
|
|
# |
|
679
|
0
|
|
|
|
|
0
|
my ( $records, $bytes, $seconds ) = ( 0, 0, times ); |
|
680
|
|
|
|
|
|
|
|
|
681
|
0
|
|
|
|
|
0
|
my $trace_record; |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
# Each record starts with a 20-byte "header-like" part containing |
|
684
|
|
|
|
|
|
|
# the record $type, a unique $stream identifier, the simulation |
|
685
|
|
|
|
|
|
|
# $time in seconds, and the number of bytes ($length) left to be |
|
686
|
|
|
|
|
|
|
# read in the current record. |
|
687
|
|
|
|
|
|
|
# |
|
688
|
0
|
|
|
|
|
0
|
my ( $type, $stream, $time, $length ); |
|
689
|
|
|
|
|
|
|
|
|
690
|
0
|
|
|
|
|
0
|
while( read($in_fh, $trace_record, 20) ) { |
|
691
|
|
|
|
|
|
|
|
|
692
|
0
|
|
|
|
|
0
|
( $type, $stream, $time, $length ) = unpack("N N B64 N", $trace_record); |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
# A $type code of 0 indicates that the current record contains a |
|
695
|
|
|
|
|
|
|
# type code. See %supported_record_types above. |
|
696
|
|
|
|
|
|
|
# |
|
697
|
0
|
0
|
|
|
|
0
|
$type == 0 && do { |
|
698
|
|
|
|
|
|
|
|
|
699
|
0
|
|
|
|
|
0
|
$records++; |
|
700
|
|
|
|
|
|
|
|
|
701
|
0
|
|
|
|
|
0
|
read($in_fh, $trace_record, $length); |
|
702
|
0
|
|
|
|
|
0
|
$bytes += $length; |
|
703
|
|
|
|
|
|
|
|
|
704
|
0
|
|
|
|
|
0
|
my ( $t_id, $t_name ) = split ' ', $trace_record; |
|
705
|
|
|
|
|
|
|
|
|
706
|
0
|
0
|
|
|
|
0
|
croak "Unsupported record type \"$t_name\"\n" |
|
707
|
|
|
|
|
|
|
unless $supported_record_types{$t_name}; |
|
708
|
|
|
|
|
|
|
|
|
709
|
0
|
0
|
|
|
|
0
|
warn "Trace contains records of type \"$t_name\"\n" if $SHOW_SOURCES; |
|
710
|
|
|
|
|
|
|
|
|
711
|
0
|
|
|
|
|
0
|
$types{$t_id} = $t_name; |
|
712
|
|
|
|
|
|
|
|
|
713
|
0
|
|
|
|
|
0
|
next; |
|
714
|
|
|
|
|
|
|
}; |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# A type code of 1 indicates that the current record contains a |
|
717
|
|
|
|
|
|
|
# stream id and the stream name, which is the NHI corresponding to |
|
718
|
|
|
|
|
|
|
# the interface being monitored. |
|
719
|
|
|
|
|
|
|
# |
|
720
|
0
|
0
|
|
|
|
0
|
$type == 1 && do { |
|
721
|
|
|
|
|
|
|
|
|
722
|
0
|
|
|
|
|
0
|
$records++; |
|
723
|
|
|
|
|
|
|
|
|
724
|
0
|
|
|
|
|
0
|
read($in_fh, $trace_record, $length); |
|
725
|
0
|
|
|
|
|
0
|
$bytes += $length; |
|
726
|
|
|
|
|
|
|
|
|
727
|
0
|
|
|
|
|
0
|
my ( $s_id, $s_name ) = split ' ', $trace_record; |
|
728
|
0
|
0
|
|
|
|
0
|
warn "Trace contains records from NHI $s_name\n" if $SHOW_SOURCES; |
|
729
|
|
|
|
|
|
|
|
|
730
|
0
|
|
|
|
|
0
|
$sources{$s_id} = $s_name; |
|
731
|
|
|
|
|
|
|
|
|
732
|
0
|
|
|
|
|
0
|
foreach my $k ( keys %plot ) { |
|
733
|
0
|
0
|
|
|
|
0
|
open( $plot_fh{$s_id}{$k}, '>', "$s_name.$plot{$k}" ) |
|
734
|
|
|
|
|
|
|
or croak "Cannot open $s_name.$plot{$k} ($!)"; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
|
|
737
|
0
|
|
|
|
|
0
|
$times{$s_id} = 0; |
|
738
|
0
|
|
|
|
|
0
|
$time_decimals{$s_id} = '%.3f'; |
|
739
|
|
|
|
|
|
|
|
|
740
|
0
|
|
|
|
|
0
|
next; |
|
741
|
|
|
|
|
|
|
}; |
|
742
|
|
|
|
|
|
|
|
|
743
|
0
|
0
|
|
|
|
0
|
$types{$type} eq 'SSF.Net.QueueProbeIntRecord' && do { |
|
744
|
|
|
|
|
|
|
|
|
745
|
0
|
|
|
|
|
0
|
$records++; |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# From the next 8 bytes only the first 4 are useful: A float |
|
748
|
|
|
|
|
|
|
# carrying the interval used by SSF.Net.droptailQueueMonitor_1 |
|
749
|
|
|
|
|
|
|
# to sample the queue size. This is due to hack in the original |
|
750
|
|
|
|
|
|
|
# Java code. |
|
751
|
|
|
|
|
|
|
# |
|
752
|
0
|
|
|
|
|
0
|
read($in_fh, $trace_record, 8); |
|
753
|
0
|
|
|
|
|
0
|
$bytes += 8; |
|
754
|
|
|
|
|
|
|
|
|
755
|
0
|
|
|
|
|
0
|
$sampling_intervals{$stream} = |
|
756
|
|
|
|
|
|
|
int_bits_to_float(unpack("V", $trace_record)); |
|
757
|
|
|
|
|
|
|
|
|
758
|
0
|
0
|
|
|
|
0
|
if ( $PRINT_EXACT_DECIMAL_DIGITS ) { |
|
759
|
|
|
|
|
|
|
# Extract the exact sampling interval from the trace, and |
|
760
|
|
|
|
|
|
|
# present the actual time in the generated output |
|
761
|
|
|
|
|
|
|
# |
|
762
|
0
|
|
|
|
|
0
|
my $d = log( $sampling_intervals{$stream} ) / log(10); |
|
763
|
0
|
0
|
|
|
|
0
|
$d = $d < 0 ? sprintf("%.0f",-$d) : 0; |
|
764
|
|
|
|
|
|
|
|
|
765
|
0
|
|
|
|
|
0
|
$time_decimals{$stream} =~ s/3/$d/; |
|
766
|
|
|
|
|
|
|
} |
|
767
|
|
|
|
|
|
|
|
|
768
|
0
|
|
|
|
|
0
|
next; |
|
769
|
|
|
|
|
|
|
}; |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
# This must be the first actual queue record. We're past the trace |
|
772
|
|
|
|
|
|
|
# preamble, so exit this loop. |
|
773
|
|
|
|
|
|
|
# |
|
774
|
0
|
|
|
|
|
0
|
last; |
|
775
|
|
|
|
|
|
|
} |
|
776
|
|
|
|
|
|
|
|
|
777
|
0
|
|
|
|
|
0
|
do {{ |
|
778
|
|
|
|
|
|
|
|
|
779
|
0
|
|
|
|
|
0
|
( $type, $stream, $time, $length ) = unpack("N N B64 N", $trace_record); |
|
|
0
|
|
|
|
|
0
|
|
|
780
|
|
|
|
|
|
|
|
|
781
|
0
|
|
|
|
|
0
|
$records++; |
|
782
|
|
|
|
|
|
|
|
|
783
|
0
|
0
|
|
|
|
0
|
$types{$type} eq 'SSF.Net.QueueRecord_1' && do { |
|
784
|
|
|
|
|
|
|
# Read the next 12 bytes, which correspond to one float |
|
785
|
|
|
|
|
|
|
# carrying the average queue length in bytes (over the |
|
786
|
|
|
|
|
|
|
# sampling interval), and two 32-bit integers: |
|
787
|
|
|
|
|
|
|
# |
|
788
|
|
|
|
|
|
|
# * the number of packets enqueued during the current sampling |
|
789
|
|
|
|
|
|
|
# interval ($pkts) |
|
790
|
|
|
|
|
|
|
# |
|
791
|
|
|
|
|
|
|
# * the number of dropped packets during the current sampling |
|
792
|
|
|
|
|
|
|
# interval ($drops) |
|
793
|
|
|
|
|
|
|
# |
|
794
|
0
|
|
|
|
|
0
|
read($in_fh, $trace_record, 12); |
|
795
|
|
|
|
|
|
|
|
|
796
|
0
|
|
|
|
|
0
|
$bytes += 12; |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# You may notice that SSF.OS.NetFlow.BytesUtil uses a custom way |
|
799
|
|
|
|
|
|
|
# for storing 32-bit integers. Integers are actually stored in |
|
800
|
|
|
|
|
|
|
# little-endian binary format, contrary to standard Java, which |
|
801
|
|
|
|
|
|
|
# is big-endian. |
|
802
|
|
|
|
|
|
|
# |
|
803
|
0
|
|
|
|
|
0
|
my ($q_length, $pkts, $drops) = unpack("V V V", $trace_record); |
|
804
|
|
|
|
|
|
|
|
|
805
|
0
|
|
|
|
|
0
|
my $t = sprintf( $time_decimals{$stream}, long_bits_to_double($time) ); |
|
806
|
|
|
|
|
|
|
|
|
807
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{pkts} } "$t $pkts\n" |
|
|
0
|
|
|
|
|
0
|
|
|
808
|
|
|
|
|
|
|
if ( $plot{pkts} ); |
|
809
|
|
|
|
|
|
|
|
|
810
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{drops} } "$t $drops\n" |
|
|
0
|
|
|
|
|
0
|
|
|
811
|
|
|
|
|
|
|
if ( $plot{drops} ); |
|
812
|
|
|
|
|
|
|
|
|
813
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{av_qlen} } |
|
|
0
|
|
|
|
|
0
|
|
|
814
|
|
|
|
|
|
|
"$t ",int_bits_to_float($q_length), "\n" |
|
815
|
|
|
|
|
|
|
if ( $plot{av_qlen} ); |
|
816
|
|
|
|
|
|
|
|
|
817
|
0
|
|
|
|
|
0
|
next; |
|
818
|
|
|
|
|
|
|
}; |
|
819
|
|
|
|
|
|
|
|
|
820
|
0
|
0
|
|
|
|
0
|
$types{$type} eq 'SSF.Net.QueueRecord_2' && do { |
|
821
|
0
|
0
|
|
|
|
0
|
if ( not defined $sampling_intervals{$stream} ) { |
|
822
|
0
|
|
|
|
|
0
|
$sampling_intervals{$stream} = long_bits_to_double($time); |
|
823
|
|
|
|
|
|
|
|
|
824
|
0
|
0
|
|
|
|
0
|
if ( $PRINT_EXACT_DECIMAL_DIGITS ) { |
|
825
|
|
|
|
|
|
|
# Extract the exact sampling interval from the trace, and |
|
826
|
|
|
|
|
|
|
# present the actual time in the generated output |
|
827
|
|
|
|
|
|
|
# |
|
828
|
0
|
|
|
|
|
0
|
my $d = log( $sampling_intervals{$stream} ) / log(10); |
|
829
|
0
|
0
|
|
|
|
0
|
$d = $d < 0 ? sprintf("%.0f",-$d) : 0; |
|
830
|
|
|
|
|
|
|
|
|
831
|
0
|
|
|
|
|
0
|
$time_decimals{$stream} =~ s/3/$d/; |
|
832
|
|
|
|
|
|
|
} |
|
833
|
|
|
|
|
|
|
} |
|
834
|
|
|
|
|
|
|
|
|
835
|
0
|
|
|
|
|
0
|
$times{$stream} += $sampling_intervals{$stream}; |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
# Read the next 16 bytes, which correspond to four 32-bit |
|
838
|
|
|
|
|
|
|
# integers: |
|
839
|
|
|
|
|
|
|
# |
|
840
|
|
|
|
|
|
|
# * the total number of packets enqueued at the interface from |
|
841
|
|
|
|
|
|
|
# the beginning of the simulation ($sumpkts) |
|
842
|
|
|
|
|
|
|
# |
|
843
|
|
|
|
|
|
|
# * the total number of packets dropped at the interface from |
|
844
|
|
|
|
|
|
|
# the beginning of the simulation ($sumdrops) |
|
845
|
|
|
|
|
|
|
# |
|
846
|
|
|
|
|
|
|
# * the number of packets enqueued during the current sampling |
|
847
|
|
|
|
|
|
|
# interval ($pkts) |
|
848
|
|
|
|
|
|
|
# |
|
849
|
|
|
|
|
|
|
# * the number of dropped packets during the current sampling |
|
850
|
|
|
|
|
|
|
# interval ($drops) |
|
851
|
|
|
|
|
|
|
# |
|
852
|
0
|
|
|
|
|
0
|
read($in_fh, $trace_record, 16); |
|
853
|
|
|
|
|
|
|
|
|
854
|
0
|
|
|
|
|
0
|
$bytes += 16; |
|
855
|
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
# SSF.OS.NetFlow.BytesUtil uses a custom way for storing |
|
857
|
|
|
|
|
|
|
# 32-bit integers. This results in integers being stored in |
|
858
|
|
|
|
|
|
|
# binary little-endian format, contrary to standard Java, |
|
859
|
|
|
|
|
|
|
# which is big-endian. |
|
860
|
|
|
|
|
|
|
# |
|
861
|
0
|
|
|
|
|
0
|
my ( $sumpkts, $sumdrops, $pkts, $drops ) |
|
862
|
|
|
|
|
|
|
= unpack("V V V V", $trace_record); |
|
863
|
|
|
|
|
|
|
|
|
864
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{pkts} } "$times{$stream} $pkts\n" |
|
|
0
|
|
|
|
|
0
|
|
|
865
|
|
|
|
|
|
|
if ( $plot{pkts} ); |
|
866
|
|
|
|
|
|
|
|
|
867
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{drops} } "$times{$stream} $drops\n" |
|
|
0
|
|
|
|
|
0
|
|
|
868
|
|
|
|
|
|
|
if ( $plot{drops} ); |
|
869
|
|
|
|
|
|
|
|
|
870
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{sumpkts} } "$times{$stream} $sumpkts\n" |
|
|
0
|
|
|
|
|
0
|
|
|
871
|
|
|
|
|
|
|
if ( $plot{sumpkts} ); |
|
872
|
|
|
|
|
|
|
|
|
873
|
0
|
0
|
|
|
|
0
|
print { $plot_fh{$stream}{sumdrops} } "$times{$stream} $sumdrops\n" |
|
|
0
|
|
|
|
|
0
|
|
|
874
|
|
|
|
|
|
|
if ( $plot{sumdrops} ) ; |
|
875
|
|
|
|
|
|
|
|
|
876
|
0
|
|
|
|
|
0
|
next; |
|
877
|
|
|
|
|
|
|
}; |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
}} while( read($in_fh, $trace_record, 20) ); |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# Display processing stats |
|
882
|
|
|
|
|
|
|
# |
|
883
|
0
|
0
|
|
|
|
0
|
if ($SHOW_STATS) { |
|
884
|
0
|
|
|
|
|
0
|
$seconds = times - $seconds ; |
|
885
|
0
|
|
|
|
|
0
|
my $rate = 'Inf'; |
|
886
|
0
|
0
|
|
|
|
0
|
if ($seconds > 0) { |
|
887
|
0
|
|
|
|
|
0
|
$rate = sprintf("%.0f", $bytes / (1024 * $seconds)); |
|
888
|
|
|
|
|
|
|
} |
|
889
|
|
|
|
|
|
|
warn |
|
890
|
0
|
|
|
|
|
0
|
"{Player processed $records records, ", |
|
891
|
|
|
|
|
|
|
$bytes, " bytes in $seconds seconds ($rate KB/s)}\n"; |
|
892
|
|
|
|
|
|
|
} |
|
893
|
|
|
|
|
|
|
|
|
894
|
0
|
|
|
|
|
0
|
return $records; |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
} # end of droptail_record_plotter() |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
#################################################################### |
|
899
|
|
|
|
|
|
|
# Utility functions |
|
900
|
|
|
|
|
|
|
#################################################################### |
|
901
|
|
|
|
|
|
|
# |
|
902
|
|
|
|
|
|
|
# int_bits_to_float() 'returns the float value corresponding to a |
|
903
|
|
|
|
|
|
|
# given bit represention. The argument is considered to be a |
|
904
|
|
|
|
|
|
|
# representation of a floating-point value according to the IEEE 754 |
|
905
|
|
|
|
|
|
|
# floating-point "single format" bit layout.' -- from |
|
906
|
|
|
|
|
|
|
# http://java.sun.com/j2se/1.4.1/docs/api/java/lang/Float.html#intBitsToFloat(int). |
|
907
|
|
|
|
|
|
|
# int_bits_to_float() is used to read a float stored in binary format |
|
908
|
|
|
|
|
|
|
# by a Java program. |
|
909
|
|
|
|
|
|
|
# |
|
910
|
|
|
|
|
|
|
sub int_bits_to_float ($) { |
|
911
|
5
|
|
|
5
|
0
|
8
|
my $i = shift; |
|
912
|
|
|
|
|
|
|
|
|
913
|
5
|
50
|
|
|
|
9
|
return '+Inf' if ( $i == 0x7f800000 ); |
|
914
|
5
|
50
|
|
|
|
11
|
return '-Inf' if ( $i == 0xff800000 ); |
|
915
|
|
|
|
|
|
|
|
|
916
|
5
|
50
|
33
|
|
|
36
|
return 'NaN' if ( $i >= 0x7f800001 and $i <= 0x7fffffff or |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
917
|
|
|
|
|
|
|
$i >= 0xffffffff and $i <= 0xff800001 ); |
|
918
|
|
|
|
|
|
|
|
|
919
|
5
|
50
|
|
|
|
11
|
my $s = ( ( $i >> 31 ) == 0 ) ? 1 : -1; |
|
920
|
5
|
|
|
|
|
9
|
my $e = ( $i >> 23 ) & 0xff; |
|
921
|
5
|
50
|
|
|
|
13
|
my $m = ( $e == 0 ) ? ( $i & 0xfffff ) << 1 |
|
922
|
|
|
|
|
|
|
: ( $i & 0x7fffff ) | 0x800000; |
|
923
|
5
|
|
|
|
|
6
|
$e -= 150; |
|
924
|
|
|
|
|
|
|
|
|
925
|
5
|
50
|
|
|
|
29
|
return ( $e >= 0 ) ? $s * $m * 2**$e |
|
926
|
|
|
|
|
|
|
: $s * $m / (2**(-$e)); |
|
927
|
|
|
|
|
|
|
} # end of int_bits_to_float() |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
# long_bits_to_double() 'returns the double value corresponding to a |
|
930
|
|
|
|
|
|
|
# given bit representation. The argument is considered to be a |
|
931
|
|
|
|
|
|
|
# representation of a floating-point value according to the IEEE 754 |
|
932
|
|
|
|
|
|
|
# floating-point "double format" bit layout.' -- from |
|
933
|
|
|
|
|
|
|
# http://java.sun.com/j2se/1.4.1/docs/api/java/lang/Double.html#longBitsToDouble(long) |
|
934
|
|
|
|
|
|
|
# long_bits_to_double() is used to read a double stored binary format |
|
935
|
|
|
|
|
|
|
# by a Java program. |
|
936
|
|
|
|
|
|
|
# |
|
937
|
|
|
|
|
|
|
sub long_bits_to_double ($) { |
|
938
|
6
|
|
|
6
|
0
|
10
|
my $i = shift; |
|
939
|
|
|
|
|
|
|
|
|
940
|
6
|
50
|
|
|
|
19
|
my $s = substr($i, 0, 1) eq '0' ? 1 : -1; |
|
941
|
6
|
|
|
|
|
12
|
my $e = oct('0b' . substr($i, 1, 11)); |
|
942
|
6
|
|
|
|
|
609
|
$e &= 0x7ff; |
|
943
|
|
|
|
|
|
|
|
|
944
|
6
|
|
|
|
|
14
|
my $m1 = oct('0b' . substr($i, 12, 20)); |
|
945
|
6
|
|
|
|
|
11
|
my $m2 = oct('0b' . substr($i, 21, 32)); |
|
946
|
|
|
|
|
|
|
|
|
947
|
6
|
|
|
|
|
8
|
my $m; |
|
948
|
|
|
|
|
|
|
|
|
949
|
6
|
100
|
|
|
|
13
|
if ( $e == 0 ) { |
|
950
|
2
|
|
|
|
|
6
|
$m = 0.0 + ($m1 * 2**32 + $m2) * 2; |
|
951
|
|
|
|
|
|
|
} |
|
952
|
|
|
|
|
|
|
else { |
|
953
|
4
|
|
|
|
|
5
|
$m1 |= 0x100000; |
|
954
|
4
|
|
|
|
|
5
|
$m = 0.0 + $m1 * 2**32 + $m2; |
|
955
|
|
|
|
|
|
|
} |
|
956
|
|
|
|
|
|
|
|
|
957
|
6
|
|
|
|
|
8
|
$e -= 1075; |
|
958
|
|
|
|
|
|
|
|
|
959
|
6
|
50
|
|
|
|
68
|
return ( $e >= 0 ) ? $s * $m * 2**$e : $s * $m / (2**(-$e)); |
|
960
|
|
|
|
|
|
|
} # end of long_bits_to_double() |
|
961
|
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
# readUTF FILEHANDLE |
|
963
|
|
|
|
|
|
|
# |
|
964
|
|
|
|
|
|
|
# This function emulates the functionality of the Java method |
|
965
|
|
|
|
|
|
|
# java.io.DataInputStream.readUTF(). It reads and returns a single |
|
966
|
|
|
|
|
|
|
# Java-UTF-8 string from FILEHANDLE. |
|
967
|
|
|
|
|
|
|
# |
|
968
|
|
|
|
|
|
|
# For more details see |
|
969
|
|
|
|
|
|
|
# http://java.sun.com/j2se/1.4.1/docs/api/java/io/DataInputStream.html#readUTF() |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
sub readUTF( * ) { |
|
972
|
6
|
|
|
6
|
0
|
8
|
my $fh = shift; |
|
973
|
6
|
|
|
|
|
6
|
my ( $string_length, $utf_string ); |
|
974
|
|
|
|
|
|
|
|
|
975
|
6
|
|
|
|
|
56
|
read($fh, $string_length, 2); |
|
976
|
6
|
|
|
|
|
18
|
read($fh, $utf_string, unpack("n", $string_length)); |
|
977
|
|
|
|
|
|
|
|
|
978
|
6
|
|
|
|
|
15
|
return $utf_string; |
|
979
|
|
|
|
|
|
|
} # end of readUTF() |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
1; |
|
983
|
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
__END__ |