-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.xml
1035 lines (741 loc) · 45.6 KB
/
index.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Corleonis</title>
<link>https://swind.github.io/index.xml</link>
<description>Recent content on Corleonis</description>
<generator>Hugo -- gohugo.io</generator>
<language>zh-tw</language>
<lastBuildDate>Wed, 27 Sep 2017 15:25:07 +0800</lastBuildDate>
<atom:link href="https://swind.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>parameter_to_property</title>
<link>https://swind.github.io/post/parameter_to_property/</link>
<pubDate>Wed, 27 Sep 2017 15:25:07 +0800</pubDate>
<guid>https://swind.github.io/post/parameter_to_property/</guid>
<description><p>在 Python package <a href="https://github.com/xiaocong/uiautomator">uiautomator</a> 看到的有趣寫法</p>
<p></p>
<h1 id="parameter-to-property">Parameter to Property</h1>
<p>在 Python package <code>uiautomator</code> 的說明文件裡面可以看到這個有趣的語法被大量使用。</p>
<p>例如</p>
<pre><code># long click on the center of the specific ui object
d(text=&quot;Settings&quot;).long_click()
# long click on the bottomright corner of the specific ui object
d(text=&quot;Settings&quot;).long_click.bottomright()
# long click on the topleft corner of the specific ui object
d(text=&quot;Settings&quot;).long_click.topleft()
</code></pre>
<p>或</p>
<pre><code># wait until the ui object appears
d(text=&quot;Settings&quot;).wait.exists(timeout=3000)
# wait until the ui object gone
d(text=&quot;Settings&quot;).wait.gone(timeout=1000)
</code></pre>
<p>本來覺的應該就只是 <code>long_click</code> 或者 <code>wait</code> 本身就是一個實做了 <code>__call__</code> 與其他 function 的物件。
只是有點費工而已。</p>
<p>但是之前有機會看了一下 uiautomator 的原始碼才發現,他是使用 python decorator 來實做的。</p>
<pre><code>def param_to_property(*props, **kwprops):
if props and kwprops:
raise SyntaxError(&quot;Can not set both props and kwprops at the same time.&quot;)
class Wrapper(object):
def __init__(self, func):
self.func = func
self.kwargs, self.args = {}, []
def __getattr__(self, attr):
if kwprops:
for prop_name, prop_values in kwprops.items():
if attr in prop_values and prop_name not in self.kwargs:
self.kwargs[prop_name] = attr
return self
elif attr in props:
self.args.append(attr)
return self
raise AttributeError(&quot;%s parameter is duplicated or not allowed!&quot; % attr)
def __call__(self, *args, **kwargs):
if kwprops:
kwargs.update(self.kwargs)
self.kwargs = {}
return self.func(*args, **kwargs)
else:
new_args, self.args = self.args + list(args), []
return self.func(*new_args, **kwargs)
return Wrapper
</code></pre>
<p><a href="https://github.com/xiaocong/uiautomator/blob/master/uiautomator/__init__.py">原始碼連結</a></p>
<p>與這個 decorator 的使用範例</p>
<pre><code>@property
def wait(self):
'''
Wait until the ui object gone or exist.
Usage:
d(text=&quot;Clock&quot;).wait.gone() # wait until it's gone.
d(text=&quot;Settings&quot;).wait.exists() # wait until it appears.
'''
@param_to_property(action=[&quot;exists&quot;, &quot;gone&quot;])
def _wait(action, timeout=3000):
if timeout / 1000 + 5 &gt; int(os.environ.get(&quot;JSONRPC_TIMEOUT&quot;, 90)):
http_timeout = timeout / 1000 + 5
else:
http_timeout = int(os.environ.get(&quot;JSONRPC_TIMEOUT&quot;, 90))
method = self.device.server.jsonrpc_wrap(
timeout=http_timeout
).waitUntilGone if action == &quot;gone&quot; else self.device.server.jsonrpc_wrap(timeout=http_timeout).waitForExists
return method(self.selector, timeout)
return _wait
</code></pre>
<p><code>param_to_property</code> 內的 <code>Wrapper</code> 基本上 <code>__call__</code> 的部份就如預期,沒有什麼意外, 但是可以注意一下 <code>self.kwargs</code> 與 <code>self.args</code> 的部份。</p>
<p>另外比較重要的就是在 <code>__getattr__</code> 與 <code>param_to_property</code> 初始化的部份。</p>
<p>舉例來說</p>
<pre><code class="language-python">@param_to_property(action=[&quot;exists&quot;, &quot;gone&quot;])
def _wait(action, timeout=3000):
</code></pre>
<p>在使用 <code>@param_to_property</code> 的時候,所帶的參數會被存入 <code>props</code> 與 <code>kwprops</code>。
以這邊的例子就是 <code>kwprops</code> 會等於</p>
<pre><code class="language-python">{
&quot;action&quot;: [&quot;exists&quot;, &quot;gone&quot;]
}
</code></pre>
<p>當呼叫 <code>d(text=&quot;Settings&quot;).wait.gone(timeout=1000)</code> 的時候,
第一個 <code>.wait</code> 其實 <code>@param_to_property</code> 所回傳的 <code>Wrapper</code>。</p>
<p>所以 <code>wait.gone</code> 的部分會呼叫</p>
<pre><code>def __getattr__(self, attr):
if kwprops:
for prop_name, prop_values in kwprops.items():
if attr in prop_values and prop_name not in self.kwargs:
self.kwargs[prop_name] = attr
return self
elif attr in props:
self.args.append(attr)
return self
raise AttributeError(&quot;%s parameter is duplicated or not allowed!&quot; % attr)
</code></pre>
<p>而 <code>attr</code> 的值就是 <code>&quot;gone&quot;</code>, 然後執行結果就是會以 key 為 <code>action</code> value 為 <code>gone</code> 的形式儲存在 <code>self.kwargs</code>。
( 因為 <code>gone</code> 有在 prop_values 中,並且 <code>action</code> 也還沒有被儲存過。)</p>
<p>然後最後在回傳一次 <code>Wrapper</code> 本身,以便使用者繼續呼叫。</p>
<p>也就是其實這個 <code>decorator</code> 可以想像成將取得 <code>property</code> 的動作, 轉換成值儲存起來。
最後在 <code>__call__</code> 的時候再將這些參數合併起來傳給真正要處理的 function。</p>
<p>我之所以會覺得有趣是因為這種寫法某種程度上的改寫了語法的概念,但是好或者不好我也不是很清楚。</p>
<p>所以做個筆記,說不定哪一天我會遇到用這個方法超合適的情況。</p></description>
</item>
<item>
<title>使用 ADB 來監控 APP 的 CPU 使用量</title>
<link>https://swind.github.io/post/android_cpu_usage_by_adb/</link>
<pubDate>Tue, 26 Sep 2017 17:53:21 +0800</pubDate>
<guid>https://swind.github.io/post/android_cpu_usage_by_adb/</guid>
<description><p>其實在 <code>Linux</code> 上監控 CPU 的方法在 <code>Android</code> 上面也可以用,並且這些方法在很多人的 Blog 與 StackOverflow 也被討論到爛掉了。
這邊主要是紀錄如何使用 <code>adb</code> 與 <code>/proc</code> 取得 <code>Process</code> 與 <code>Thread</code> 的 CPU 資訊。</p>
<p></p>
<h1 id="整體的-cpu-使用量">整體的 CPU 使用量</h1>
<p>要取得整個 Device 的 CPU 資訊的話,可以透過 <code>/proc/stat</code>。</p>
<p>使用指令 <code>adb shell cat /proc/stat</code> 會看到</p>
<pre><code>cpu 1286215 71763 779091 4271301 16007 9 16100 0 0 0
cpu0 560338 33626 409552 2845856 9346 0 12458 0 0 0
cpu1 80441 6912 40265 146206 1396 4 379 0 0 0
cpu2 174491 9981 89521 383939 1992 3 838 0 0 0
cpu3 470945 21244 239753 895300 3273 2 2425 0 0 0
intr 56252464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11183406 0 0 0 0 0 0 0 0 0 0 0 0 0 165872 1 0 0 0 0 0 2426472 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2184 8 5 0 10 0 0 0 2025880 0 0 0 0 0 0 0 0 0 0 384654 0 0 0 0 5 689 3 2 3 0 0 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2283211 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 91049 1142156 0 0 4891 0 0 0 0 60 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 632514 0 12 0 0 0 0 0 5607578 0 0 0 0 0 0 365 0 0 0 36028 5 0 2027914 2134764 0 0 0 0 0 0 0 0 0 2043721 0 5 0 0 0 0 748 263 0 0 0 1584739 0 0 75 0 0 0 0 0 0 0 0 0 0 0 0 11817 0 0 0 0 0 207 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7067 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 16 0 0 0 0 86 0 0 0 0 0 0 0 0 0 0 0 5062 481 1198 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 215 12 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 198100180
btime 1506325560
processes 344110
procs_running 1
procs_blocked 0
softirq 30624059 14285 6147433 22588 1777078 14285 14285 400466 3969871 2347 18261421
</code></pre>
<p>像密碼表一樣的資訊,不過我們可以只著重在第一行的 <code>cpu</code> 即可。</p>
<pre><code>cpu 1286215 71763 779091 4271301 16007 9 16100 0 0 0
</code></pre>
<p>每個欄位代表的意思是</p>
<table>
<thead>
<tr>
<th></th>
<th>user</th>
<th>nice</th>
<th>system</th>
<th>idle</th>
<th>iowait</th>
<th>irq</th>
<th>softirq</th>
<th>stealstolen</th>
<th>guest</th>
<th>guest_nice</th>
</tr>
</thead>
<tbody>
<tr>
<td>cpu</td>
<td>1286215</td>
<td>71763</td>
<td>779091</td>
<td>4271301</td>
<td>16007</td>
<td>9</td>
<td>16100</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
<p>這些數字都是從 Device 啟動之後的累計值, 單位是 <code>jiffies</code>,
因此計算在某段時間內的值的話。
就必須在起始跟結束的時候都呼叫一次 <code>adb shell cat /proc/stat</code>,
並且把所有得到的數字相減。</p>
<p>例如</p>
<p>開始的時候取得</p>
<pre><code>cpu 1430106 74780 838424 4391260 16309 9 16780 0 0 0
</code></pre>
<p>結束的時候取得</p>
<pre><code>cpu 1431109 74806 838854 4392567 16313 9 16783 0 0 0
</code></pre>
<p>那麼這段時間總共是 <code>2773 jiffies</code></p>
<pre><code># after - before
(1431109 + 74806 + 838854 + 4392567 + 16313 + 9 + 16783 + 0 + 0 + 0) - (1430106 + 74780 + 838424 + 4391260 + 16309 + 9 + 16780 + 0 + 0 + 0)
= 2773
</code></pre>
<h1 id="process-的-cpu-使用率">Process 的 CPU 使用率</h1>
<p>有了整體之後,再來就是針對 <code>Process</code> , 計算 <code>Process</code> 之前需要先取得 <code>Process</code> 的 <code>PID</code>。
最簡單的方法就是使用 <code>ps</code> 來取得。</p>
<pre><code>adb shell ps | grep {package name}
u0_a174 11458 460 1781352 365668 sys_epoll_ 00000000 S com.android.chrome
</code></pre>
<p>第二個 <code>11458</code> 就是 <code>PID</code></p>
<p>有了 PID 之後, 就可以透過 <code>/proc/{PID}/stat</code> 取得 Process 的 CPU 使用時間</p>
<pre><code>adb shell cat /proc/11458/stat
11458 (mosa.puffinFree) S 460 460 0 0 -1 4194624 13380248 0 334 0 120663 47337 0 0 16 -4 133 0 9367583 1535365120 62981 4294967295 1 1 0 0 0 0 4612 0 38648 4294967295 0 0 17 3 0 0 0 0 0 0 0
</code></pre>
<p>Process 的資料量明顯的多很多,可以參考 <a href="http://man7.org/linux/man-pages/man5/proc.5.html">PROC(5)</a> 中的 <code>/proc/[pid]/stat</code> 來取得每個欄位的意思。
這邊我們只要注意第 14 跟第 15 個。</p>
<pre><code> (14) utime %lu
Amount of time that this process has been scheduled
in user mode, measured in clock ticks (divide by
sysconf(_SC_CLK_TCK)). This includes guest time,
guest_time (time spent running a virtual CPU, see
below), so that applications that are not aware of
the guest time field do not lose that time from
their calculations.
(15) stime %lu
Amount of time that this process has been scheduled
in kernel mode, measured in clock ticks (divide by
sysconf(_SC_CLK_TCK)).
</code></pre>
<p>將這兩個值相加就是 <code>到目前為止</code> Process 所使用的 CPU 時間。
所以跟前面一樣,需要在開始與結束各取一次,並且<strong>相減</strong>。</p>
<h1 id="thread">Thread</h1>
<p>在 <code>/proc/{PID}/task</code> 底下會有一堆名稱是數字的資料夾</p>
<pre><code>adb shell ls /proc/11458/task
10143
11458
11463
11465
11466
11468
.
.
.
</code></pre>
<p>每個數字都是這個 Process 底下 Thread 的 <code>tid</code>, 在這些資料夾裡面也有一個 <code>stat</code> 的檔案。
所以跟 Process 一樣只要使用</p>
<pre><code>adb shell cat /proc/11458/task/10143/stat
10143 (OkHttp Connecti) S 460 460 0 0 -1 4194368 267 0 0 0 0 1 0 0 20 0 129 0 9491883 1599287296 67669 4294967295 1 1 0 0 0 0 4612 0 38648 4294967295 0 0 -1 3 0 0 0 0 0 0 0 0
</code></pre>
<p>就會取得跟 Process 一樣格式的內容,我們一樣需要注意 <code>utime</code> 與 <code>stime</code> 但是除此之外還需要注意一個部份。
就是第一個欄位的 tid <code>10143</code> 與 thread name <code>(OkHttp Connecti)</code>。
由於 thread name 可能會重複,但是 tid 會不相同,所以最好是將 tid 與 thread name 合成一個 key 比較不容易統計錯誤。</p>
<p>將這兩個值相加就是 <code>到目前為止</code> Thread 所使用的 CPU 時間。
所以跟前面一樣,需要在開始與結束各取一次,並且<strong>相減</strong>。</p>
<h1 id="cpu-使用率">CPU 使用率</h1>
<p>就將前面透過 <code>/proc/stat</code> 取得的值當分母,去計算 Process 與個 Thread 的百分比就可以了。</p></description>
</item>
<item>
<title>用 devpi 建立私有的 pypi server</title>
<link>https://swind.github.io/post/devpi/</link>
<pubDate>Wed, 20 Sep 2017 11:16:26 +0800</pubDate>
<guid>https://swind.github.io/post/devpi/</guid>
<description><p>簡短作個筆記</p>
<h1 id="dockerfile">Dockerfile</h1>
<p><a href="https://github.com/Swind/docker-devpi">docker-devpi</a> fork 自 <a href="https://github.com/saily/openshift-devpi">saily/openshift-devpi</a>
不過如果可以的話我希望有機會可以改用 <code>alpine</code>。</p>
<p></p>
<pre><code class="language-yaml">FROM python:3.6
ENV DEVPI_SERVERDIR=/mnt/server \
DEVPI_CLIENTDIR=/mnt/client \
DEVPI_MIRROR_CACHE_EXPIRY=86400
COPY [&quot;requirements.txt&quot;, &quot;logger_cfg.json&quot;, &quot;run.sh&quot;, &quot;/&quot;]
RUN pip install --no-cache-dir -r /requirements.txt &amp;&amp; \
rm /requirements.txt
VOLUME /mnt
EXPOSE 3141
CMD &quot;/run.sh&quot;
</code></pre>
<p>CMD 所執行的 <code>run.sh</code> 在 repository 裡面有。</p>
<h1 id="pip-conf">pip.conf</h1>
<p><code>pip.conf</code> 的位置在 <code>~/.pip/pip.conf</code></p>
<p>將<code>ip</code>, <code>port</code> 與 <code>path</code> 替換成自家 server 的位置。</p>
<pre><code class="language-config">[global]
index-url = http://{ip}:{port}/{path}/+simple/
trusted-host = {ip}
[search]
index = http://{ip}:{port}/cloudmosa/dev/
</code></pre>
<h1 id="devpi-client-指令說明">devpi client 指令說明</h1>
<p>參考 devpi 官網的 <a href="https://devpi.net/docs/devpi/devpi/stable/%2Bd/index.html">Quickstart: uploading, testing, pushing releases</a></p>
<h2 id="安裝-devpi-client">安裝 devpi client</h2>
<pre><code class="language-sh">pip install -U devpi-client
</code></pre>
<h2 id="指定-devpi-server">指定 devpi server</h2>
<pre><code class="language-sh">devpi use http://localhost:3141
</code></pre>
<pre><code>using server: http://localhost:3141/ (not logged in)
no current index: type 'devpi use -l' to discover indices
~/.pydistutils.cfg : http://localhost:4040/alice/dev/+simple/
~/.pip/pip.conf : http://localhost:4040/alice/dev/+simple/
~/.buildout/default.cfg: http://localhost:4040/alice/dev/+simple/
always-set-cfg: no
</code></pre>
<h2 id="建立-user-與-login">建立 User 與 Login</h2>
<pre><code class="language-sh">devpi user -c testuser password=123
devpi login testuser --password=123
</code></pre>
<h2 id="建立與指定要使用的-index">建立與指定要使用的 index</h2>
<p>我記得我在這邊有遇到權限的問題,看起來使用者權限的部份還需要詳細讀一下文件。</p>
<pre><code class="language-sh">devpi index -c dev bases=root/pypi
devpi use testuser/dev
</code></pre></description>
</item>
<item>
<title>python-zeroconf 範例</title>
<link>https://swind.github.io/post/python-zeroconf/</link>
<pubDate>Mon, 11 Sep 2017 13:55:48 +0800</pubDate>
<guid>https://swind.github.io/post/python-zeroconf/</guid>
<description><h1 id="zeroconf">zeroconf</h1>
<p>zeroconf ( Zero configuration networking ),主要透過 mDNS ( Multicast DNS ) 與 DNS-SD ( DNS Service Discovery ) 來實現。
而 <a href="https://github.com/jstasiak/python-zeroconf">python-zeroconf</a> 就是 <code>zeroconf</code> 的 <strong>純 Python</strong> 實現。</p>
<p></p>
<p>在 Apple 的機器上的實做就是 <code>Bonjour</code>。</p>
<p>而 Linux 則是 <code>Avachi</code></p>
<p>關於 <code>zeroconf</code> 詳細的介紹可以參考 <a href="http://bbs.mico.io/card/1236">设备发现之Bonjour协议原理分享</a></p>
<h1 id="server">Server</h1>
<p><code>python-zeroconf</code> 的 <code>README.md</code> 裡面雖然沒有提到 <code>mDNS</code> 的部份,但是其實在 <a href="https://github.com/jstasiak/python-zeroconf/blob/master/examples/registration.py">examples</a>
有個簡單的範例。</p>
<p>下面是稍微整理一下 <code>examples</code> 裡面的範例</p>
<pre><code class="language-python">from zeroconf import ServiceInfo, Zeroconf
import socket
import logging
logger = logging.getLogger(__name__)
class Server:
def __init__(self, name, address, port, desc):
self.zeroconf = Zeroconf()
self.name = name
desc = {
'description': desc
}
self.info = ServiceInfo(type_=&quot;_http._tcp.local.&quot;,
name=&quot;{}._http._tcp.local.&quot;.format(name),
address=socket.inet_aton(address),
port=port,
weight=0,
priority=0,
properties=desc)
def register(self):
self.zeroconf.register_service(self.info)
def unregister(self):
self.zeroconf.unregister_service(self.info)
def close(self):
self.zeroconf.close()
if __name__ == &quot;__main__&quot;:
server = Server(
name=&quot;_testwebsite&quot;,
address=&quot;127.0.0.1&quot;,
port=9999,
desc=&quot;0.0.1&quot;)
server.register()
import time
try:
time.sleep(600)
except KeyboardInterrupt:
pass
finally:
server.unregister()
server.close()
</code></pre>
<p>主要要看的就只有 <code>ServiceInfo</code> 跟呼叫 <code>zeroconf.register_service</code> 的部份而已</p>
<h1 id="client">Client</h1>
<p>Client 的部份一樣有 <a href="https://github.com/jstasiak/python-zeroconf/blob/master/examples/browser.py">examples</a> 可以參考。</p>
<pre><code class="language-python">from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf
from threading import Event
import socket
import time
import logging
logger = logging.getLogger(&quot;chatroom&quot;)
class Service:
ADDED = &quot;added&quot;
REMOVED = &quot;removed&quot;
def __init__(self, zeroconf, service_type, name, state_change):
self.state = None
self.name = name
self.zeroconf = zeroconf
self._update_info(service_type, name)
self._state_change(state_change)
def _update_info(self, service_type, name):
info = self.zeroconf.get_service_info(service_type, name)
self.address = socket.inet_ntoa(info.address)
self.port = info.port
self.weight = info.weight
self.priority = info.priority
self.server_name = info.server
if info.properties:
self.version = info.properties.get(&quot;version&quot;)
else:
self.version = None
def __str__(self):
return &quot;[{}] {}:{} {}&quot;.format(self.state, self.address, self.port, self.name)
def _state_change(self, state_change):
if state_change is ServiceStateChange.Added:
self.state = self.ADDED
elif state_change is ServiceStateChange.Removed:
self.state = self.REMOVED
class Browser:
def __init__(self):
self.zeroconf = Zeroconf()
self.handlers = []
self.services = {}
def list(self):
return self.services.values()
def get(self, name):
return self.services.get(name)
def wait(self, name):
event = Event()
def wait_handler(service):
if name == service.name:
event.set()
self.unregister_handler(wait_handler)
self.register_handler(wait_handler)
if name in self.services:
event.set()
self.unregister_handler(wait_handler)
return event
def handler(self, zeroconf, service_type, name, state_change):
service = Service(zeroconf, service_type, name, state_change)
if service.state == service.ADDED:
self.services[name] = service
else:
del self.services[name]
for handler in self.handlers:
handler(service)
def register_handler(self, handler):
self.handlers.append(handler)
def unregister_handler(self, handler):
self.handlers.remove(handler)
def start(self):
self.browser = ServiceBrowser(self.zeroconf, &quot;_tcp.local.&quot;, handlers=[self.handler])
def close(self):
self.zeroconf.close()
if __name__ == &quot;__main__&quot;:
browser = Browser()
def handler(state):
print(state)
browser.register_handlers(handler)
browser.start()
time.sleep(600)
</code></pre>
<p>比較不一樣的是,<code>ServiceBrowser</code> 會建立一個 <code>Thread</code> 去取得資訊,並且當有 state 變化的時候(ADDED 或 REMOVED) 時,呼叫註冊的 handlers。</p></description>
</item>
<item>
<title>使用 adb 來追蹤查看 APP 的網路流量</title>
<link>https://swind.github.io/post/use-adb-to-track-the-network-statistics/</link>
<pubDate>Wed, 12 Jul 2017 10:46:29 +0800</pubDate>
<guid>https://swind.github.io/post/use-adb-to-track-the-network-statistics/</guid>
<description><h2 id="使用-proc-底下的資訊">使用 /proc 底下的資訊</h2>
<p>雖然 Android 7 之後似乎一般應用程式已經沒有辦法存取 <code>/proc</code> 底下的東西了。 但是使用 ADB 的話還是沒有問題的。</p>
<p>這次我們使用的目標是</p>
<ol>
<li><code>/proc/uid_stat/{uid}/tcp_rcv</code></li>
<li><code>/proc/uid_stat/{uid}/tcp_snd</code></li>
</ol>
<p><code>tcp_rcv</code> 與 <code>tcp_snd</code> 分別所紀錄的是該 uid 所使用的 <code>TCP</code> 傳輸量(注意 <code>UDP</code> 並不包含在內)</p>
<p></p>
<h2 id="取得-app-的-uid">取得 APP 的 uid</h2>
<p>取得 APP 的 uid 比較方便的方式有兩種</p>
<h3 id="啟動-app-透過-ps-取得-pid-再從-proc-來查詢-uid">啟動 APP 透過 ps 取得 pid 再從 /proc 來查詢 uid</h3>
<ol>
<li>啟動 APP</li>
<li>執行 <code>adb shell ps | grep {app 的 package 關鍵字}</code>,例如 <code>adb shell ps | grep chrome</code> 就可以查詢到 Chrome 的 <code>pid</code></li>
</ol>
<pre><code class="language-bash">&gt; adb shell ps | grep chrome
u0_a96 25910 460 1089096 104992 sys_epoll_ 00000000 S com.android.chrome
u0_a96 25960 460 1011240 65940 sys_epoll_ 00000000 S com.android.chrome:privileged_process0
u0_i1 26047 460 1045356 65916 sys_epoll_ 00000000 S com.android.chrome:sandboxed_process0
</code></pre>
<p>我們只要使用只有 <code>package name</code> 的 <code>pid</code> 就可以了。以此為例,就是 <code>25910</code></p>
<ol>
<li>透過 <code>adb shell cat /proc/{pid}/status</code> 查詢 <code>uid</code></li>
</ol>
<pre><code class="language-bash">&gt; adb shell cat /proc/25910/status
Name: .android.chrome
State: S (sleeping)
Tgid: 25910
Pid: 25910
PPid: 460
TracerPid: 0
Uid: 10096 10096 10096 10096
Gid: 10096 10096 10096 10096
FDSize: 256
Groups: 3003 9997 50096
VmPeak: 1590940 kB
VmSize: 1068072 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 130672 kB
VmRSS: 114316 kB
VmData: 180240 kB
VmStk: 8192 kB
VmExe: 20 kB
VmLib: 107956 kB
VmPTE: 424 kB
VmSwap: 0 kB
Threads: 49
SigQ: 0/11726
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000001204
SigIgn: 0000000000000000
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: fffffff000000000
Cpus_allowed: f
Cpus_allowed_list: 0-3
voluntary_ctxt_switches: 171855
nonvoluntary_ctxt_switches: 22283
</code></pre>
<p>從上面 <code>Uid</code> 欄位可以得知 Chrome 的 <code>uid</code> 是 <code>10096</code></p>
<h3 id="透過-dumpsys-與-package-name-查詢">透過 dumpsys 與 package name 查詢</h3>
<p><code>adb shell &quot;dumpsys package {package 名稱} | grep userId&quot;</code></p>
<pre><code class="language-bash">&gt; adb shell &quot;dumpsys package com.android.chrome | grep userId&quot;
userId=10096
</code></pre>
<h2 id="透過-proc-uid-stat-uid-查詢-tcp-的傳輸量">透過 <code>/proc/uid_stat/{uid}</code> 查詢 TCP 的傳輸量</h2>
<p><strong>透過 TCP 送出的流量 (bytes)</strong></p>
<pre><code class="language-bash">&gt; adb shell cat /proc/uid_stat/10096/tcp_snd
11120
</code></pre>
<p><strong>透過 TCP 接收的流量 (bytes)</strong></p>
<pre><code class="language-bash">&gt; adb shell cat /proc/uid_stat/10096/tcp_rcv
597160
</code></pre>
<h2 id="透過-proc-net-xt-qtaguid-stats-查詢傳輸量">透過 <code>/proc/net/xt_qtaguid/stats</code> 查詢傳輸量</h2>
<p>有些 Device 沒有 <code>uid_stat</code> (例如 Galaxy S8+) 所以另外一個方法就是使用 <code>xt_qtaguid</code> 來查詢。
這個方法一樣要使用到 <code>uid</code>, 指令為</p>
<p><code>adb shell cat /proc/net/xt_qtaguid/stats | grep {uid}</code></p>
<p>e.g.</p>
<pre><code class="language-bash">&gt; adb shell cat /proc/net/xt_qtaguid/stats | grep 10029
72 tun0 0x0 10229 0 1627251 1728 123455 1525 1627251 1728 0 0 0 0 123455 1525 0 0 0 0
73 tun0 0x0 10229 1 341358394 381688 21708316 286335 341358394 381688 0 0 0 0 21381218 281971 327098 4364 0 0
288 wlan0 0x0 10229 0 387926 4640 506630 4639 387926 4640 0 0 0 0 506630 4639 0 0 0 0
289 wlan0 0x0 10229 1 63445949 170110 13704545 156389 63445949 170110 0 0 0 0 13572037 154582 132508 1807 0 0
488 wlan0 0xffffff0400000000 10229 0 18079 17 1444 14 18079 17 0 0 0 0 1444 14 0 0 0 0
489 wlan0 0xffffff0400000000 10229 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
504 lo 0x0 10229 0 17712 372 12376 238 17712 372 0 0 0 0 12376 238 0 0 0 0
505 lo 0x0 10229 1 297993712 91716 298233535 92271 297993712 91716 0 0 0 0 298233535 92271 0 0 0 0
</code></pre>
<p>以空白作為分隔,每一欄的數值所代表的意思為</p>
<p><code>idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_t cp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets</code></p>
<p>我們所需要的只有 <code>tx_bytes</code> 與 <code>rx_bytes</code></p>
<p>如果只有需要 TCP/UDP 的話也可以順便查詢 <code>tx_udp_bytes</code> 或 <code>tx_tcp_bytes</code> 與 <code>rx_udp_bytes</code> 或 <code>rx_tcp_bytes</code>。</p>
<p>所以修改一下剛剛的指令,增加 <code>cut</code> 來取得我所想要的欄位</p>
<p><code>adb shell cat /proc/net/xt_qtaguid/stats | grep {uid} | cut -d &quot; &quot; -f 2,3,4,5,6,8</code></p>
<pre><code>&gt; adb shell cat /proc/net/xt_qtaguid/stats | grep 10229 | cut -d &quot; &quot; -f 2,3,4,5,6,8
wlan0 0x0 10096 0 0 0
wlan0 0x0 10096 1 15659531 414473
</code></pre>
<ol>
<li><code>iface</code> 代表所使用的網路界面,例如 <code>tun0</code> 應該就是 VPN</li>
<li><code>acct_tag_hex</code> 開發人員可以使用 <code>TrafficStats.setThreadStatsTag</code> 標記某個 Thread,這樣之後就可以在 xt_qtaguid 裡面依照 <code>acct_tag_hex</code> 來區分是哪個 Thread 所使用的流量。</li>
<li><code>uid</code></li>
<li><code>cnt_set</code> 0 代表 APP 的背景程式, 1 代表 APP 的前景程式</li>
<li><code>rx_bytes</code> 代表所接收的 bytes 數</li>
<li><code>tx_bytes</code> 代表送出去的 bytes 數</li>
</ol>
<h2 id="參考資料">參考資料</h2>
<ol>
<li><a href="https://stackoverflow.com/questions/12904809/tracking-an-applications-network-statistics-netstats-using-adb">Tracking an application&rsquo;s network statistics (netstats) using ADB</a></li>
<li><a href="http://www.cnblogs.com/FallenHWer/p/3359359.html">Android 獲得 UID 的三個辦法</a></li>
<li><a href="http://www.voidcn.com/blog/focusjava/article/p-6152550.html">Android 流量優化(一): 模塊化流量統計</a></li>
</ol></description>
</item>
<item>
<title>ADB 傳輸格式的筆記</title>
<link>https://swind.github.io/post/adb/</link>
<pubDate>Tue, 11 Jul 2017 10:46:29 +0800</pubDate>
<guid>https://swind.github.io/post/adb/</guid>
<description><p>緩慢補充中&hellip;</p>
<p></p>
<h2 id="adb-傳輸格式">ADB 傳輸格式</h2>
<p>在傳送指令給 <code>ADB Server</code> 的時候,
需要在開頭使用 4 個 16進位的數字表示後面要傳遞的資料長度。</p>
<p>例如要列出所有 Devices 的時候,
所使用的指令是 <code>host:devices</code>。
所以加上長度資訊就會變成 <code>000Chost:devices</code>。</p>
<p><code>host:devices</code> 是 12 個字所以用 16 進位表示長度就是 <code>000C</code>。</p>
<p>下面是 Python 負責轉換指令的範例程式碼</p>
<pre><code class="language-python">def encode(cmd):
length = &quot;{0:04X}{}&quot;.format(len(cmd))
return &quot;{}{}&quot;.format(length, cmd)
if __name__ == &quot;__main__&quot;:
print(encode(&quot;host:devices&quot;))
</code></pre>
<p>而 ADB Server 在收到指令之後,會立刻回傳一個長度為 4 bytes 的結果。
這個結果指的是<strong>傳送指令的結果</strong>,而不是<strong>執行指令的結果</strong>。
如果接收指令沒有任何問題的話,會回傳 <code>OKAY</code>。</p>
<p>如果前 4 個 bytes 不是 <code>OKAY</code> 的話,
那麼他後面就會帶著 Error Message,要記得把他一起讀完。</p>
<h2 id="adb-的指令">ADB 的指令</h2>
<p>ADB 的傳輸內容大致上可以分為幾類</p>
<ol>
<li>host</li>
<li>host-serial</li>
<li>host-transport</li>
</ol>
<p>當你想透過 ADB 對某個 Device 下指令的時候,需要先使用 <code>host:transport:{serialno}</code> 來切換目標。
而 <code>host</code> 底下的指令則是針對 ADB Server 的指令,所以不需要進行 <code>host:transport</code>。</p>
<h3 id="host">host</h3>
<h4 id="connect">connect</h4>
<p>讓 ADB Server 連線到位於 {host}:{port} 的 Device ( 需要設定 Device 讓他的 Adb daemon 可以透過網路連接 )</p>
<p><code>host:connect:{host}:{port}</code></p>
<p>e.g.</p>
<p><code>host:connect:192.168.1.63:5555</code></p>
<h4 id="devices">devices</h4>
<p>列出所有 Devices,也就是 <code>adb devices</code></p>
<p><code>host:devices</code></p>
<h4 id="disconnect">disconnect</h4>
<p>中斷與某台 Device 的連線</p>
<p><code>host:disconnect:{hostla}:{port}</code></p>
<h4 id="kill">kill</h4>
<p>叫 Adb Service 自殺</p>
<p><code>host:kill</code></p>
<h4 id="track-devices">track-devices</h4>
<p><code>host:track-devices</code></p>
<p>將這個連線轉為專門 track devices 的連線,當 ADB Server 發現有 Device 被新增/移除的時候。
就會將最新的 Device List 傳送過來。</p>
<h4 id="transport">transport</h4>
<p><code>host:track-devices:&lt;serial-number&gt;</code></p>
<p>transport 中最重要的指令,跟 ADB Service 說之後將這條連線的指令全都送到 <code>serial-number</code> 這台機器或者模擬器上面。</p>
<h3 id="version">version</h3>
<p>整個 Protocol 的指令一共有以下幾種</p>
<pre><code class="language-python">class Protocol:
OKAY = 'OKAY'
FAIL = 'FAIL'
STAT = 'STAT'
LIST = 'LIST'
DENT = 'DENT'
RECV = 'RECV'
DATA = 'DATA'
DONE = 'DONE'
SEND = 'SEND'
QUIT = 'QUIT'
@staticmethod
def decode_length(length):
return int(length, 16)
@staticmethod
def encode_length(length):
return &quot;{0:04X}&quot;.format(length)
@staticmethod
def encode_data(data):
b_data = data.encode('utf-8')
b_length = Protocol.encode_length(len(b_data)).encode('utf-8')
return b&quot;&quot;.join([b_length, b_data])
</code></pre>
<h1 id="參考資料">參考資料</h1>
<ol>
<li><a href="https://github.com/openstf/adbkit">adbkit</a></li>
<li><a href="https://android.googlesource.com/platform/system/core/+/master/adb/protocol.txt">adb protocol</a></li>
<li><a href="https://android.googlesource.com/platform/system/core/+/master/adb/SERVICES.TXT">adb services</a></li>
</ol></description>
</item>
<item>
<title>使用 docker 與 docker-compose 啟動 Jenkins</title>
<link>https://swind.github.io/post/jenkins-html-report/</link>
<pubDate>Tue, 18 Apr 2017 11:51:34 +0800</pubDate>
<guid>https://swind.github.io/post/jenkins-html-report/</guid>
<description><h2 id="docker-hub-jenkins">Docker Hub - Jenkins</h2>
<p><a href="https://hub.docker.com/_/jenkins/">Docker Hub -Jenkins</a> 上其實文章已經很完整了。
這邊只是把我整理成 docker-compose 的過程紀錄一下。</p>
<p></p>
<h2 id="在-docker-jenkins-內使用-docker">在 Docker Jenkins 內使用 Docker</h2>
<p>有點饒舌,但是其實就只是要讓 Docker Jenkins 的 Master ( 這個 Master 是跑在 Docker 裡面的 ) 可以使用 Docker 指令而已。
當然更好的作法是使用 <code>Slave</code> 負責需要使用 Docker 的 Job。</p>
<p>所以這邊紀錄的不是很正統的執行方式。</p>
<h3 id="在-jenkins-docker-image-裡面安裝-docker-compose-指令">在 Jenkins docker image 裡面安裝 docker-compose 指令</h3>
<p>建立一個新的 <code>Dockerfile</code> 來建立新的包含 Docker 與 docker-compose 指令的 Jenkins Images。</p>
<pre><code>FROM jenkins:latest
USER root
RUN apt-get update
RUN apt-get install -y sudo
RUN rm -rf /var/lib/apt/lists/*
RUN echo &quot;jenkins ALL=NOPASSWD: ALL&quot; &gt;&gt; /etc/sudoers
RUN curl -L &quot;https://github.com/docker/compose/releases/download/1.11.2/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose
RUN ls /usr/local/bin
RUN chmod +x /usr/local/bin/docker-compose
</code></pre>
<h3 id="啟動-jenkins-的-docker-compose-yml">啟動 Jenkins 的 docker-compose.yml</h3>
<pre><code class="language-yaml">version: '2'
services:
jenkins:
build: .
ports:
- &quot;80:8080&quot;
- &quot;50000:50000&quot;
volumes:
- ./jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
environment:
- JAVA_OPTS='-Dhudson.model.DirectoryBrowserSupport.CSP=&quot;&quot;'
</code></pre>
<p>這個設定檔的重點只有在 <code>/var/run/docker.sock:/var/run/docker.sock</code> 與 <code>/usr/bin/docker:/usr/bin/docker</code>。
由於 docker daemon 並不是執行在 container 裡面,並且在 container 裡面再啟動一個 container 怎麼想都不合理。</p>
<p>所以這邊的作法其實是把 Host 的 Docker 環境塞進去 Container 裡面讓他可以操作而已。</p>
<p>之後這個 Container 裡面的 Master 應該就可以使用 Docker 與 docker-compose 指令了</p></description>
</item>
<item>
<title>建立可以執行 python3 與 adb 指令的 docker images</title>
<link>https://swind.github.io/post/docker-python3-adb/</link>
<pubDate>Wed, 12 Apr 2017 14:40:30 +0800</pubDate>
<guid>https://swind.github.io/post/docker-python3-adb/</guid>
<description><h2 id="python3-adb">Python3 + adb</h2>
<p>這個 Image 使用 Alpine Linux Image 安裝了 Python3 與 ADB。
整體大小約 9x MB,用於執行 Python3 撰寫的 Android 測試。</p>
<p>整個 <code>Dockerfile</code> 是各家 Dockerfile 的大雜燴。感謝</p>
<ul>
<li><a href="https://github.com/frol/docker-alpine-python3">frol/docker-alpine-python3</a>,</li>
<li><a href="https://github.com/sorccu/docker-adb">sorccu/docker-adb</a></li>
<li><a href="https://github.com/cdrx/docker-pyinstaller">cdrx/docker-pyinstaller</a></li>
</ul>
<p></p>
<h2 id="原始碼與-docker-image">原始碼與 Docker Image</h2>