Skip to content

Commit 600d3e6

Browse files
author
Matthieu Morel
committed
Add writev syscall
1 parent c4b98a4 commit 600d3e6

File tree

6 files changed

+140
-4
lines changed

6 files changed

+140
-4
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/madvise
3434
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/chdir
3535
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/mkdir
3636
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/rmdir
37+
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/writev
3738

3839
.PHONY: example-programs
3940
example-programs: $(EXAMPLE_PROGRAMS)

example-programs/writev.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <sys/uio.h>
5+
#include <unistd.h>
6+
7+
int main(int argc, char *const argv[])
8+
{
9+
// example taken from man for writev(2):
10+
char *str0 = "hello ";
11+
char *str1 = "world\n";
12+
struct iovec iov[2];
13+
ssize_t nwritten;
14+
15+
iov[0].iov_base = str0;
16+
iov[0].iov_len = strlen(str0);
17+
iov[1].iov_base = str1;
18+
iov[1].iov_len = strlen(str1);
19+
20+
nwritten = writev(STDOUT_FILENO, iov, 2);
21+
printf("%ld\n", nwritten);
22+
printf("%p\n", iov[0].iov_base);
23+
printf("%p\n", iov[1].iov_base);
24+
25+
if (nwritten == -1) {
26+
perror("writev");
27+
}
28+
return nwritten;
29+
}

src/System/Hatrace.hs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ module System.Hatrace
4040
, SyscallExitDetails_faccessat(..)
4141
, SyscallEnterDetails_write(..)
4242
, SyscallExitDetails_write(..)
43+
, SyscallEnterDetails_writev(..)
44+
, SyscallExitDetails_writev(..)
4345
, SyscallEnterDetails_read(..)
4446
, SyscallExitDetails_read(..)
4547
, SyscallEnterDetails_close(..)
@@ -193,7 +195,7 @@ import Foreign.Marshal.Alloc (alloca)
193195
import Foreign.Marshal.Array (withArray)
194196
import qualified Foreign.Marshal.Array (peekArray)
195197
import Foreign.Marshal.Utils (withMany)
196-
import Foreign.Ptr (castPtr, Ptr, nullPtr, wordPtrToPtr)
198+
import Foreign.Ptr (castPtr, Ptr, nullPtr, wordPtrToPtr, intPtrToPtr)
197199
import Foreign.Storable (Storable, peekByteOff, sizeOf)
198200
import GHC.Stack (HasCallStack, callStack, getCallStack, prettySrcLoc)
199201
import System.Directory (canonicalizePath, doesFileExist, findExecutable)
@@ -1813,6 +1815,26 @@ instance SyscallExitFormatting SyscallExitDetails_rmdir where
18131815
syscallExitToFormatted SyscallExitDetails_rmdir{ enterDetail } =
18141816
(syscallEnterToFormatted enterDetail, NoReturn)
18151817

1818+
data SyscallEnterDetails_writev = SyscallEnterDetails_writev
1819+
{ fd :: CInt
1820+
, count :: CSize
1821+
-- Peeked details
1822+
, iovs :: [IovecStruct]
1823+
, bufContents :: [ByteString]
1824+
} deriving (Eq, Ord, Show)
1825+
1826+
instance SyscallEnterFormatting SyscallEnterDetails_writev where
1827+
syscallEnterToFormatted SyscallEnterDetails_writev{ fd, iovs, bufContents, count } =
1828+
FormattedSyscall "writev" [formatArg fd, argPlaceholder "*iovec", formatArg iovs, formatArg bufContents, formatArg count]
1829+
1830+
data SyscallExitDetails_writev = SyscallExitDetails_writev
1831+
{ enterDetail :: SyscallEnterDetails_writev
1832+
, writtenCount :: CSize
1833+
} deriving (Eq, Ord, Show)
1834+
1835+
instance SyscallExitFormatting SyscallExitDetails_writev where
1836+
syscallExitToFormatted SyscallExitDetails_writev{ enterDetail, writtenCount } =
1837+
(syscallEnterToFormatted enterDetail, formatReturn writtenCount)
18161838

18171839
data DetailedSyscallEnter
18181840
= DetailedSyscallEnter_open SyscallEnterDetails_open
@@ -1823,6 +1845,7 @@ data DetailedSyscallEnter
18231845
| DetailedSyscallEnter_access SyscallEnterDetails_access
18241846
| DetailedSyscallEnter_faccessat SyscallEnterDetails_faccessat
18251847
| DetailedSyscallEnter_write SyscallEnterDetails_write
1848+
| DetailedSyscallEnter_writev SyscallEnterDetails_writev
18261849
| DetailedSyscallEnter_read SyscallEnterDetails_read
18271850
| DetailedSyscallEnter_execve SyscallEnterDetails_execve
18281851
| DetailedSyscallEnter_close SyscallEnterDetails_close
@@ -1885,6 +1908,7 @@ data DetailedSyscallExit
18851908
| DetailedSyscallExit_access SyscallExitDetails_access
18861909
| DetailedSyscallExit_faccessat SyscallExitDetails_faccessat
18871910
| DetailedSyscallExit_write SyscallExitDetails_write
1911+
| DetailedSyscallExit_writev SyscallExitDetails_writev
18881912
| DetailedSyscallExit_read SyscallExitDetails_read
18891913
| DetailedSyscallExit_execve SyscallExitDetails_execve
18901914
| DetailedSyscallExit_close SyscallExitDetails_close
@@ -2043,6 +2067,17 @@ getSyscallEnterDetails syscall syscallArgs pid = let proc = TracedProcess pid in
20432067
, count = fromIntegral count
20442068
, bufContents
20452069
}
2070+
Syscall_writev -> do
2071+
let SyscallArgs{ arg0 = fd, arg1 = iovPtr, arg2 = iovcnt } = syscallArgs
2072+
n = fromIntegral $ min iovcnt $ fromIntegral (maxBound :: Int)
2073+
iovs <- peekArray (TracedProcess pid) n (word64ToPtr iovPtr :: Ptr IovecStruct)
2074+
contents <- mapM (\IovecStruct{ iov_base = base, iov_len = len } -> peekBytes proc (intPtrToPtr $ fromIntegral base) (fromIntegral len)) iovs
2075+
pure $ DetailedSyscallEnter_writev $ SyscallEnterDetails_writev
2076+
{ fd = fromIntegral fd
2077+
, iovs = iovs
2078+
, count = fromIntegral iovcnt
2079+
, bufContents = contents
2080+
}
20462081
Syscall_read -> do
20472082
let SyscallArgs{ arg0 = fd, arg1 = bufAddr, arg2 = count } = syscallArgs
20482083
let bufPtr = word64ToPtr bufAddr
@@ -2577,6 +2612,11 @@ getSyscallExitDetails detailedSyscallEnter result pid =
25772612
pure $ DetailedSyscallExit_write $
25782613
SyscallExitDetails_write{ enterDetail, writtenCount = fromIntegral result }
25792614

2615+
DetailedSyscallEnter_writev
2616+
enterDetail@SyscallEnterDetails_writev{} -> do
2617+
pure $ DetailedSyscallExit_writev $
2618+
SyscallExitDetails_writev{ enterDetail, writtenCount = fromIntegral result }
2619+
25802620
DetailedSyscallEnter_access
25812621
enterDetail@SyscallEnterDetails_access{} -> do
25822622
pure $ DetailedSyscallExit_access $
@@ -2880,7 +2920,9 @@ peekArray pid size ptr
28802920
let (tmpPtr, _, _) = BSI.toForeignPtr arrayBytes
28812921
withForeignPtr tmpPtr (\p -> Foreign.Marshal.Array.peekArray size (castPtr p))
28822922
where
2883-
elemSize = sizeOf ptr
2923+
elemSize = sizeOf (valueOf ptr)
2924+
valueOf :: Ptr a -> a
2925+
valueOf = undefined
28842926

28852927
readPipeFds :: CPid -> Ptr CInt -> IO (CInt, CInt)
28862928
readPipeFds pid pipefd = do
@@ -3277,6 +3319,8 @@ formatSyscallEnter enterDetails =
32773319

32783320
DetailedSyscallEnter_write details -> syscallEnterToFormatted details
32793321

3322+
DetailedSyscallEnter_writev details -> syscallEnterToFormatted details
3323+
32803324
DetailedSyscallEnter_read details -> syscallEnterToFormatted details
32813325

32823326
DetailedSyscallEnter_close details -> syscallEnterToFormatted details
@@ -3430,6 +3474,8 @@ formatDetailedSyscallExit detailedExit handleUnimplemented =
34303474

34313475
DetailedSyscallExit_write details -> formatDetails details
34323476

3477+
DetailedSyscallExit_writev details -> formatDetails details
3478+
34333479
DetailedSyscallExit_read details -> formatDetails details
34343480

34353481
DetailedSyscallExit_close details -> formatDetails details

src/System/Hatrace/Format.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import qualified Data.Text.Encoding.Error as TE
2626
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
2727
import Data.Void (Void)
2828
import Data.Word (Word64)
29-
import Foreign.C.Types (CShort(..), CUShort(..), CInt(..), CUInt(..), CLong(..), CULong(..), CSize(..), CTime(..))
29+
import Foreign.C.Types (CShort(..), CUShort(..), CInt(..), CUInt(..), CLong(..), CULong(..), CSize(..), CTime(..), CUIntPtr)
3030
import Foreign.Ptr (Ptr, nullPtr, ptrToIntPtr)
3131
import System.Posix.Types (CMode(..), CPid(..), CUid(..), CGid(..))
3232

@@ -98,6 +98,9 @@ instance ArgFormatting CTime where
9898
instance ArgFormatting (Ptr Void) where
9999
formatArg = formatPtrArg "void"
100100

101+
instance ArgFormatting CUIntPtr where
102+
formatArg = IntegerArg . fromIntegral
103+
101104
formatPtrArg :: String -> Ptr a -> FormattedArg
102105
formatPtrArg type_ p
103106
| p == nullPtr = FixedStringArg "NULL"

src/System/Hatrace/Types.hsc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ module System.Hatrace.Types
5959
, RLimitType(..)
6060
, ResourceType(..)
6161
, RLimitStruct(..)
62+
, IovecStruct(..)
6263
) where
6364

6465
import Control.Monad (filterM)
6566
import Data.Bits
6667
import Data.List (intercalate)
6768
import Data.Map (lookup)
6869
import Data.Maybe (catMaybes)
69-
import Foreign.C.Types (CShort(..), CUShort(..), CInt(..), CUInt(..), CLong(..), CULong(..))
70+
import Foreign.C.Types (CShort(..), CUShort(..), CInt(..), CUInt(..), CLong(..), CULong(..), CUIntPtr(..))
7071
import Foreign.Marshal (copyBytes)
7172
import Foreign.Marshal.Array (peekArray, pokeArray)
7273
import Foreign.Ptr (plusPtr, castPtr)
@@ -1729,3 +1730,23 @@ $(deriveEnumTypeClasses ''RLimitType
17291730
, ('ResourceRTPrio, (#const RLIMIT_RTPRIO), "RLIMIT_RTPRIO")
17301731
, ('ResourceRTTime, (#const RLIMIT_RTTIME), "RLIMIT_RTTIME")
17311732
])
1733+
1734+
data IovecStruct = IovecStruct
1735+
{ iov_base :: CUIntPtr -- ^ Starting address
1736+
, iov_len :: CULong -- ^ Number of bytes to transfer
1737+
} deriving (Eq, Ord, Show)
1738+
1739+
instance ArgFormatting IovecStruct where
1740+
formatArg (IovecStruct {..}) =
1741+
StructArg [("iov_base", formatArg iov_base), ("iov_len", formatArg iov_len)]
1742+
1743+
instance Storable IovecStruct where
1744+
sizeOf _ = #{size struct iovec}
1745+
alignment _ = #{alignment struct iovec}
1746+
peek p = do
1747+
iov_base <- #{peek struct iovec, iov_base} p
1748+
iov_len <- #{peek struct iovec, iov_len} p
1749+
return IovecStruct{..}
1750+
poke p IovecStruct{..} = do
1751+
#{poke struct iovec, iov_base} p iov_base
1752+
#{poke struct iovec, iov_len} p iov_len

test/HatraceSpec.hs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Control.Monad.IO.Class (liftIO)
1212
import Control.Monad.IO.Unlift (MonadUnliftIO)
1313
import Data.ByteString (ByteString)
1414
import qualified Data.ByteString as BS
15+
import qualified Data.ByteString.Char8 as B8
1516
import Data.Conduit
1617
import qualified Data.Conduit.Combinators as CC
1718
import qualified Data.Conduit.List as CL
@@ -116,6 +117,41 @@ spec = before_ assertNoChildren $ do
116117
exitCode `shouldSatisfy` \x ->
117118
x `elem` [ExitFailure 11, ExitFailure (128+11)]
118119

120+
describe "writev" $ do
121+
it "cant peek correct values of struct iov and number of byte written" $ do
122+
callProcess "make" ["--quiet", "example-programs-build/writev"]
123+
124+
argv <- procToArgv "example-programs-build/writev" []
125+
(exitCode, events) <-
126+
sourceTraceForkExecvFullPathWithSink argv $
127+
syscallExitDetailsOnlyConduit .| CL.consume
128+
exitCode `shouldBe` ExitSuccess
129+
130+
let [(stdinReads, (iov1:iov2:_), writtenCount_)] =
131+
[ (bufContents, iovs, writtenCount)
132+
| (_pid
133+
, Right (
134+
DetailedSyscallExit_writev
135+
SyscallExitDetails_writev
136+
{ enterDetail = SyscallEnterDetails_writev{ fd = 1, bufContents, iovs }
137+
, writtenCount
138+
}
139+
)
140+
) <- events
141+
]
142+
-- The example program prints out some expected values, we retrieve them relying on the write syscall
143+
let (expectedCount:expectedVect1:expectedVect2:_) = [ bufContents |
144+
(_pid, Right (
145+
DetailedSyscallExit_write
146+
SyscallExitDetails_write{ enterDetail = SyscallEnterDetails_write { bufContents } }
147+
)
148+
) <- events
149+
]
150+
stdinReads `shouldBe` ["hello ", "world\n"]
151+
fromIntegral writtenCount_ `shouldBe` (read (B8.unpack expectedCount) :: Int)
152+
fromIntegral (iov_base iov1) `shouldBe` (read (B8.unpack expectedVect1) :: Int)
153+
fromIntegral (iov_base iov2) `shouldBe` (read (B8.unpack expectedVect2) :: Int)
154+
119155
describe "sourceRawTraceForkExecvFullPathWithSink" $ do
120156

121157
it "lets the process finish if the sink exits early" $ do

0 commit comments

Comments
 (0)