2018年9月25日 星期二

[點網][超速譯]VS2017 的中斷點

https://blogs.msdn.microsoft.com/visualstudio/2018/09/13/how-can-i-pause-my-code-in-visual-studio-breakpoints-faq/

Visual Studio 是我用過最好用的 IDE,沒有之一。
中斷點這件事也是我看過不少碼農不甚使用的事。正好有官方提出一些小技巧給大家知道。
快速摘譯重點,成為超速譯的一篇。


設立 breakpoint

(1)左點左邊界 或是 按F9
(2)按F5

管理 breakpoint

(1)breakpoint window
(2) Debug -> Window -> Breakpoints

Conditional Breakpoint

(1)設定 breakpoint
(2)鼠標飄到 breakpoint 上,按下齒輪圖示
(3)選擇 Conditions,然後設定條件
(4)條件輸入完畢,關閉設定窗

Iteration breakpoint

(1)設定 breakpoint
(2)鼠標飄到 breakpoint 上,按下齒輪圖示
(3)選擇 Conditions,然後設定條件為 Hit Count


Function breakpoint

(1) Debug -> New Breakpoint -> Break at Function


Value change breakpoint

* C++, data breakpoints
* Watch Window or the Breakpoints Window 右點 變數 選擇 Break when value changes
* managed code, 針對某個 instance 的屬性偵測改變
(1)在 break mode,右點物件選擇 Make Object ID
(2)在欲偵測的屬性 setter 加入一個 conditional breakpoint 條件是 this == $1
(3)按F5,會停在 setter
(4)在 Call Stack 雙點前一個 frame 可以看到改變屬性的 code 是哪一行


exception breakpoint

在 Exception Settings 窗,設定哪些 exception 要停下來


call stack breakpoint

(1)Debug -> Windows -> Call Statck
(2)右點 calling function,選擇 Breakpoint -> Insert Breakpoint


disassembly breakpoint

(1)打開 disassembly window, Debug -> Windows -> Disassembly
(2)左點左邊界或按F9

2018年9月13日 星期四

[python] WARNING:tornado.general:Invalid multipart/form-data

用 flask 自帶的 server,有時就是會發出 500 之後就無法動作,要重開一次。
想說試試用 tornado 來裝 flask。結果就發現一個 warning。

WARNING:tornado.general:Invalid multipart/form-data
查了一下,找到 tornado 原始碼 http://www.tornadoweb.org/en/stable/_modules/tornado/httputil.html,發現應該是 Content-Disposition 只要不是 form-data 就叫。
於是再找一下定義 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition

> Only the value form-data, as well as the optional directive name and filename, can be used in the HTTP context.
attachment 只能用在非 http 的情況。好吧,應該是要改成 form-data 才合規定。就改吧

## 同場加映
header 裡的的資料若是超過 ISO-8859-1 還是有救。
剛好在 Content-Disposition 定義找到
https://tools.ietf.org/html/rfc5987
ex: foo: bar; title*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates

## tornado 原始碼部份
def parse_multipart_form_data(boundary, data, arguments, files):
    """Parses a ``multipart/form-data`` body.

    The ``boundary`` and ``data`` parameters are both byte strings.
    The dictionaries given in the arguments and files parameters
    will be updated with the contents of the body.

    .. versionchanged:: 5.1

       Now recognizes non-ASCII filenames in RFC 2231/5987
       (``filename*=``) format.
    """
    # The standard allows for the boundary to be quoted in the header,
    # although it's rare (it happens at least for google app engine
    # xmpp).  I think we're also supposed to handle backslash-escapes
    # here but I'll save that until we see a client that uses them
    # in the wild.
    if boundary.startswith(b'"') and boundary.endswith(b'"'):
        boundary = boundary[1:-1]
    final_boundary_index = data.rfind(b"--" + boundary + b"--")
    if final_boundary_index == -1:
        gen_log.warning("Invalid multipart/form-data: no final boundary")
        return
    parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n")
    for part in parts:
        if not part:
            continue
        eoh = part.find(b"\r\n\r\n")
        if eoh == -1:
            gen_log.warning("multipart/form-data missing headers")
            continue
        headers = HTTPHeaders.parse(part[:eoh].decode("utf-8"))
        disp_header = headers.get("Content-Disposition", "")
        disposition, disp_params = _parse_header(disp_header)
        if disposition != "form-data" or not part.endswith(b"\r\n"):
            gen_log.warning("Invalid multipart/form-data")
            continue
        value = part[eoh + 4:-2]
        if not disp_params.get("name"):
            gen_log.warning("multipart/form-data value missing name")
            continue
        name = disp_params["name"]
        if disp_params.get("filename"):
            ctype = headers.get("Content-Type", "application/unknown")
            files.setdefault(name, []).append(HTTPFile(  # type: ignore
                filename=disp_params["filename"], body=value,
                content_type=ctype))
        else:
            arguments.setdefault(name, []).append(value)


2018年9月12日 星期三

[點網] process.MainWindowHandle == 0


## 原因
為了拿到 process 的 MainWindowHandle 作隱藏/顯示。
但是在隱藏之後,process 的 MainWindowHandle 會等於 0


## 使用 AttachConsole GetConsoleWindow FreeConsole
其中一個方法是針對 console app 的作法是接到 console,拿到其 consolewindow 要到 ID,再離開。
使用 AttachConsole GetConsoleWindow FreeConsole

宣告需要:
```
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
int GetSmsdMainWindowHandle(int procID)
    Int32 tryHandle = 0;
    try
    {
        AttachConsole((uint)procID);
        tryHandle = GetConsoleWindow().ToInt32();
        FreeConsole();
    }
    catch (Exception)
    {
        //pass
    }
    finally
    {
        FreeConsole();
    }
    return tryHandle;
}
```

使用方法:
```
int smsdMainWindowHandle = GetSmsdMainWindowHandle(xProc.Id);
```


## 使用 EnumChildWindows GetWindowThreadProcessId
另一個方法是列舉所有 子window 再拿到其 window thread 的 process ID。
使用 EnumChildWindows GetWindowThreadProcessId

宣告需要:
```
private struct SearchData
{
    // You can put any vars in here...         
    public int currentTaskID;
    public IntPtr currenthWnd;
}
private delegate bool EnumWindowsProc(IntPtr hWnd, ref SearchData data);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, ref SearchData data);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int ProcessId);
bool EnumProc(IntPtr hWnd, ref SearchData data)
{
    int lProcID;
    GetWindowThreadProcessId(hWnd,out lProcID);
    if (lProcID == data.currentTaskID)
    {
        data.currenthWnd = hWnd;
    }
    return true;
}
```

使用方法:
```
SearchData n = new SearchData();
n.currentTaskID = xProc.Id;
EnumChildWindows(IntPtr.Zero,new EnumWindowsProc(EnumProc),ref n);
int smsdMainWindowHandle = (int)n.currenthWnd;
```

這個方法的 VB Code 由 Cyrus 提供:
```
Dim currenthWnd = 0
Public Function fEnumWindowsCallback(ByVal hWnd As Integer, ByVal lpData As Integer) As Integer
    fEnumWindowsCallback = 1
    Dim lProcID As Integer
    Call GetWindowThreadProcessId(hWnd, lProcID)
    If lProcID = currentTaskID Then
        currenthWnd = hWnd
    End If
End Function

Call EnumChildWindows(0&, AddressOf fEnumWindowsCallback, 0&)
If currenthWnd <> 0 Then
    If goSECSInterface.GEMObject.HideSMSD = True Then
        ShowWindow(currenthWnd, SW_HIDE)
    Else
        ShowWindow(currenthWnd, SW_NORMAL)
    End If
End If
```

## 參考:

  • * https://stackoverflow.com/questions/8949652/do-windows-api-enumwindows-and-enumchildwindows-functions-behave-differently-in

2018年9月11日 星期二

[pi-nas]raspberry pi glusterfs 換硬碟

在樹莓派上裝 gluster,硬碟壞了要換掉。查了幾種方式要來試試。但是想先試個最無腦的方法,就是 umount 舊硬碟,mount 新硬碟,看看有沒有比較方便。以下就是過程。

步驟 1
停止 gluster daemon
`sudo systemctl stop glusterfs-server`
這個只能讓 umount /mnt/gv0 成功,umount /data/brick1 還是不行
glusterfsd 還是佔著,查詢誰在用檔案或掛載 `fuser -m -u /data/brick1`。
用 `sudo /etc/init.d/glusterfs-server stop` https://gluster.readthedocs.io/en/latest/Administrator%20Guide/Start%20Stop%20Daemon/ 也不行
最後只好使用 `kill -9 PID` 處理掉,比較溫和應該用 `kill -15 PID`
umount /data/brick1 成功

步驟 2
拔掉舊的硬碟,插入新的硬碟,格式化
`sudo mkfs.xfs -i size=512 /dev/sda1`

修改 fstab `PARTUUID=c121432b-52f5-4bb2-a260-0d38992df858 /data/brick1 xfs defaults 1 2` 其中 PARTUUID 用 `blkid /dev/sda1` 查詢

`sudo mount -a`

`sudo mkdir /data/brick1/gv0`

到這,試試重開。

步驟 3
檢查 heal 狀況 `sudo gluster volume heal gv0 info`

```
pi@pi315:~ $ sudo gluster volume heal gv0 info
Brick pi314:/data/brick1/gv0
Status: Connected
Number of entries: 0
Brick pi315:/data/brick1/gv0
Status: Transport endpoint is not connected
Number of entries: -
```

看來直換是沒有,還需要下些指令。glusterfsd 3.9.0 有 reset-brick 看起來很好用,但!
我的 glusterfsd 是 3.8.8-1 沒有支援 reset-brick。
自身 replace `sudo gluster volume replace-brick gv0 pi315:/data/brick1/gv0 pi315:/data/brick1/gv0 commit force` 也不行。
所以一定要換個新名字,所以改成 gv0_1:
`sudo gluster volume replace-brick gv0 pi315:/data/brick1/gv0 pi315:/data/brick1/gv0_1 commit force`

這個時候,pi315 的 glusterfsd 才起得來

```
pi@pi314:~ $ sudo gluster volume status
Status of volume: gv0
Gluster process                             TCP Port  RDMA Port  Online  Pid
------------------------------------------------------------------------------
Brick pi314:/data/brick1/gv0                49153     0          Y       1492
Brick pi315:/data/brick1/gv0_1              49153     0          Y       1846
Self-heal Daemon on localhost               N/A       N/A        Y       26710
Self-heal Daemon on pi315                   N/A       N/A        Y       1851
Task Status of Volume gv0
------------------------------------------------------------------------------
There are no active volume tasks
```


然後,heal 就自動開始了。
查看 heal 狀態 `sudo gluster volume heal gv0 info`

到這其實就結束了,但是如果很在意那個 gv0_1 的話,我在 heal 結束之後又執行了一次 `replace-brick` 只是把 gv0_1 又換成 gv0。要考慮硬碟空間要足夠兩倍大。看硬碟閃,還是從 pi314 那裡拿資料。

參考:
## 有故意使用 setfattr 強迫 heal
https://serverfault.com/questions/710220/how-do-you-add-a-replacement-hdd-to-a-glusterfs-volume
https://lists.gluster.org/pipermail/gluster-users/2014-August/018515.html
https://docs.gluster.org/en/v3/Administrator%20Guide/Managing%20Volumes/#replace-brick

## 只使用 sudo gluster volume replace-brick 舊brick 新brick
https://sysadmins.co.za/replace-faulty-bricks-in-glusterfs/

## 使用 reset-brick
https://docs.gluster.org/en/latest/release-notes/3.9.0/