|
56 | 56 | DLT_EMPTY_FILE_ERROR = "DLT TRACE FILE IS EMPTY" |
57 | 57 | cDLT_FILE_NOT_OPEN_ERROR = "Could not open DLT Trace file (libdlt)" # pylint: disable=invalid-name |
58 | 58 |
|
| 59 | +DLT_UDP_MULTICAST_FD_BUFFER_SIZE = int(os.environ.get("PYDLT_UDP_MULTICAST_FD_BUFFER_SIZE", 2 * (2**20))) # 2 Mb |
| 60 | +DLT_UDP_MULTICAST_BUFFER_SIZE = int(os.environ.get("PYDLT_UDP_MULTICAST_BUFFER_SIZE", 8 * (2**20))) # 8 Mb |
| 61 | + |
59 | 62 |
|
60 | 63 | class cached_property(object): # pylint: disable=invalid-name |
61 | 64 | """ |
@@ -865,13 +868,23 @@ def __len__(self): |
865 | 868 |
|
866 | 869 |
|
867 | 870 | class DLTClient(cDltClient): |
868 | | - """DLTClient class takes care about correct initialization and |
869 | | - cleanup |
870 | | - """ |
| 871 | + """DLTClient class takes care about correct initialization and cleanup""" |
871 | 872 |
|
872 | 873 | verbose = 0 |
873 | 874 |
|
874 | 875 | def __init__(self, **kwords): |
| 876 | + """Initialize a DLTClient. |
| 877 | +
|
| 878 | + :param servIP: Optional[str] - dlt server IP. |
| 879 | + :param hostIP: Optional[str] - Only available for udp multicast mode. |
| 880 | + Set host interface address. |
| 881 | + :param port: Optional[int] - dlt tcp daemon port. |
| 882 | + :param verbose: Optional[bool] - Enable verbose output. |
| 883 | + :param udp_fd_buffer_size_bytes: Optional[int] - Only available for udp |
| 884 | + multicast mode. Set the UDP buffer size through setsockopt (unit: bytes). |
| 885 | + :param udp_buffer_size_bytes: Optional[int] - Only available for udp |
| 886 | + multicast mode. Set the DltReceiver's buffer size (unit: bytes). |
| 887 | + """ |
875 | 888 | self.is_udp_multicast = False |
876 | 889 | self.verbose = kwords.pop("verbose", 0) |
877 | 890 | if dltlib.dlt_client_init(ctypes.byref(self), self.verbose) == DLT_RETURN_ERROR: |
@@ -915,6 +928,9 @@ def __init__(self, **kwords): |
915 | 928 | # it ourselves elsewhere |
916 | 929 | self.port = kwords.get("port", DLT_DAEMON_TCP_PORT) |
917 | 930 |
|
| 931 | + self._udp_fd_buffer_size_bytes = kwords.get("udp_fd_buffer_size_bytes", DLT_UDP_MULTICAST_FD_BUFFER_SIZE) |
| 932 | + self._udp_buffer_size_bytes = kwords.get("udp_buffer_size_bytes", DLT_UDP_MULTICAST_BUFFER_SIZE) |
| 933 | + |
918 | 934 | def connect(self, timeout=None): |
919 | 935 | """Connect to the server |
920 | 936 |
|
@@ -973,7 +989,9 @@ def connect(self, timeout=None): |
973 | 989 | else: |
974 | 990 | if self.verbose: |
975 | 991 | logger.info("Connecting DLTClient using UDP Connection") |
| 992 | + |
976 | 993 | connected = dltlib.dlt_client_connect(ctypes.byref(self), self.verbose) |
| 994 | + self._set_udp_multicast_buffer_size() |
977 | 995 |
|
978 | 996 | if self.verbose: |
979 | 997 | logger.info("DLT Connection return: %s", connected) |
@@ -1051,6 +1069,54 @@ def client_loop(self): |
1051 | 1069 | dltlib.dlt_client_register_message_callback(self.msg_callback) |
1052 | 1070 | dltlib.dlt_client_main_loop(ctypes.byref(self), None, self.verbose) |
1053 | 1071 |
|
| 1072 | + def _set_udp_multicast_buffer_size(self, custom_fd_buffer_size_bytes=None, custom_buffer_size_bytes=None) -> None: |
| 1073 | + fd_buffer_size = int(self._udp_fd_buffer_size_bytes or custom_fd_buffer_size_bytes or 0) |
| 1074 | + buffer_size_bytes = int(self._udp_buffer_size_bytes or custom_buffer_size_bytes or 0) |
| 1075 | + |
| 1076 | + if fd_buffer_size: |
| 1077 | + # Socket options are associated with an open file description. This |
| 1078 | + # means that file descriptors duplicated as a consequence of dup() |
| 1079 | + # (or similar) or fork() share the same set of socket options. |
| 1080 | + # -- Chapter 61.9 Socket Options. |
| 1081 | + # The Linux Programming Interface, p.1279 |
| 1082 | + # |
| 1083 | + # The buffer size can be changed with a new fd which is created by |
| 1084 | + # dup system call (it's the internal implementation in |
| 1085 | + # `socket.fromfd`), so the code creates a socket instance first |
| 1086 | + # configures it and directly close it. |
| 1087 | + with socket.fromfd(self.sock, socket.AF_INET, socket.SOCK_DGRAM) as conf_socket: |
| 1088 | + logger.debug("Set UDP Multicast socket buffer size: %s kbytes", fd_buffer_size / 1024) |
| 1089 | + conf_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, fd_buffer_size) |
| 1090 | + |
| 1091 | + real_buffer_size = int(conf_socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) / 2) |
| 1092 | + if real_buffer_size != fd_buffer_size: |
| 1093 | + logger.warning( |
| 1094 | + ( |
| 1095 | + "Failed to set UDP Multicast buffer size. set_size: %s, real_size: %s. " |
| 1096 | + "Bypass the error and continue" |
| 1097 | + ), |
| 1098 | + fd_buffer_size / 1024, |
| 1099 | + real_buffer_size / 1024, |
| 1100 | + ) |
| 1101 | + logger.warning( |
| 1102 | + ( |
| 1103 | + "Please run command `sysctl -w net.core.rmem_max=%s` with root permission to " |
| 1104 | + "set the maximum size and restart dlt again." |
| 1105 | + ), |
| 1106 | + fd_buffer_size, |
| 1107 | + ) |
| 1108 | + |
| 1109 | + if buffer_size_bytes: |
| 1110 | + logger.debug("Set UDP Multicast DltReceiver buffer size: %s kbytes", buffer_size_bytes / 1024) |
| 1111 | + ret = dltlib.dlt_receiver_init( |
| 1112 | + ctypes.byref(self.receiver), self.sock, self.receiver.type, buffer_size_bytes |
| 1113 | + ) |
| 1114 | + if ret < 0: |
| 1115 | + raise RuntimeError( |
| 1116 | + f"Failed to set UDP Multicast DltReceiver buffer size. return code: {ret}, " |
| 1117 | + f"buffer_size_bytes: {buffer_size_bytes}" |
| 1118 | + ) |
| 1119 | + |
1054 | 1120 |
|
1055 | 1121 | def py_dlt_file_main_loop(dlt_reader, limit=None, callback=None): |
1056 | 1122 | """Main loop to read dlt messages from dlt file.""" |
|
0 commit comments