Skip to content

Commit

Permalink
Add writev syscall
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthieu Morel committed Jun 14, 2020
1 parent c4b98a4 commit c8d1346
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 3 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/madvise
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/chdir
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/mkdir
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/rmdir
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/writev

.PHONY: example-programs
example-programs: $(EXAMPLE_PROGRAMS)
Expand Down
30 changes: 30 additions & 0 deletions example-programs/writev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>

int main(int argc, char *const argv[])
{
// example taken from man for writev(2):
char *str0 = "hello ";
char *str1 = "world\n";
struct iovec iov[2];
ssize_t nwritten;

iov[0].iov_base = str0;
iov[0].iov_len = strlen(str0);
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1);

nwritten = writev(STDOUT_FILENO, iov, 2);
printf("%ld\n", nwritten);
printf("%p\n", iov[0].iov_base);
printf("%p\n", iov[1].iov_base);

if (nwritten == -1) {
perror("writev");
exit(1);
}
exit(0);
}
48 changes: 47 additions & 1 deletion src/System/Hatrace.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ module System.Hatrace
, SyscallExitDetails_faccessat(..)
, SyscallEnterDetails_write(..)
, SyscallExitDetails_write(..)
, SyscallEnterDetails_writev(..)
, SyscallExitDetails_writev(..)
, SyscallEnterDetails_read(..)
, SyscallExitDetails_read(..)
, SyscallEnterDetails_close(..)
Expand Down Expand Up @@ -1813,6 +1815,26 @@ instance SyscallExitFormatting SyscallExitDetails_rmdir where
syscallExitToFormatted SyscallExitDetails_rmdir{ enterDetail } =
(syscallEnterToFormatted enterDetail, NoReturn)

data SyscallEnterDetails_writev = SyscallEnterDetails_writev
{ fd :: CInt
, count :: CSize
-- Peeked details
, iovs :: [IovecStruct]
, bufContents :: [ByteString]
} deriving (Eq, Ord, Show)

instance SyscallEnterFormatting SyscallEnterDetails_writev where
syscallEnterToFormatted SyscallEnterDetails_writev{ fd, iovs, bufContents, count } =
FormattedSyscall "writev" [formatArg fd, argPlaceholder "*iovec", formatArg iovs, formatArg bufContents, formatArg count]

data SyscallExitDetails_writev = SyscallExitDetails_writev
{ enterDetail :: SyscallEnterDetails_writev
, writtenCount :: CSize
} deriving (Eq, Ord, Show)

instance SyscallExitFormatting SyscallExitDetails_writev where
syscallExitToFormatted SyscallExitDetails_writev{ enterDetail, writtenCount } =
(syscallEnterToFormatted enterDetail, formatReturn writtenCount)

data DetailedSyscallEnter
= DetailedSyscallEnter_open SyscallEnterDetails_open
Expand All @@ -1823,6 +1845,7 @@ data DetailedSyscallEnter
| DetailedSyscallEnter_access SyscallEnterDetails_access
| DetailedSyscallEnter_faccessat SyscallEnterDetails_faccessat
| DetailedSyscallEnter_write SyscallEnterDetails_write
| DetailedSyscallEnter_writev SyscallEnterDetails_writev
| DetailedSyscallEnter_read SyscallEnterDetails_read
| DetailedSyscallEnter_execve SyscallEnterDetails_execve
| DetailedSyscallEnter_close SyscallEnterDetails_close
Expand Down Expand Up @@ -1885,6 +1908,7 @@ data DetailedSyscallExit
| DetailedSyscallExit_access SyscallExitDetails_access
| DetailedSyscallExit_faccessat SyscallExitDetails_faccessat
| DetailedSyscallExit_write SyscallExitDetails_write
| DetailedSyscallExit_writev SyscallExitDetails_writev
| DetailedSyscallExit_read SyscallExitDetails_read
| DetailedSyscallExit_execve SyscallExitDetails_execve
| DetailedSyscallExit_close SyscallExitDetails_close
Expand Down Expand Up @@ -2043,6 +2067,17 @@ getSyscallEnterDetails syscall syscallArgs pid = let proc = TracedProcess pid in
, count = fromIntegral count
, bufContents
}
Syscall_writev -> do
let SyscallArgs{ arg0 = fd, arg1 = iovPtr, arg2 = iovcnt } = syscallArgs
n = fromIntegral $ min iovcnt $ fromIntegral (maxBound :: Int)
iovs <- peekArray (TracedProcess pid) n (word64ToPtr iovPtr :: Ptr IovecStruct)
contents <- mapM (\IovecStruct{ iov_base = base, iov_len = len } -> peekBytes proc base (fromIntegral len)) iovs
pure $ DetailedSyscallEnter_writev $ SyscallEnterDetails_writev
{ fd = fromIntegral fd
, iovs = iovs
, count = fromIntegral iovcnt
, bufContents = contents
}
Syscall_read -> do
let SyscallArgs{ arg0 = fd, arg1 = bufAddr, arg2 = count } = syscallArgs
let bufPtr = word64ToPtr bufAddr
Expand Down Expand Up @@ -2577,6 +2612,11 @@ getSyscallExitDetails detailedSyscallEnter result pid =
pure $ DetailedSyscallExit_write $
SyscallExitDetails_write{ enterDetail, writtenCount = fromIntegral result }

DetailedSyscallEnter_writev
enterDetail@SyscallEnterDetails_writev{} -> do
pure $ DetailedSyscallExit_writev $
SyscallExitDetails_writev{ enterDetail, writtenCount = fromIntegral result }

DetailedSyscallEnter_access
enterDetail@SyscallEnterDetails_access{} -> do
pure $ DetailedSyscallExit_access $
Expand Down Expand Up @@ -2880,7 +2920,9 @@ peekArray pid size ptr
let (tmpPtr, _, _) = BSI.toForeignPtr arrayBytes
withForeignPtr tmpPtr (\p -> Foreign.Marshal.Array.peekArray size (castPtr p))
where
elemSize = sizeOf ptr
elemSize = sizeOf (valueOf ptr)
valueOf :: Ptr a -> a
valueOf = undefined

readPipeFds :: CPid -> Ptr CInt -> IO (CInt, CInt)
readPipeFds pid pipefd = do
Expand Down Expand Up @@ -3277,6 +3319,8 @@ formatSyscallEnter enterDetails =

DetailedSyscallEnter_write details -> syscallEnterToFormatted details

DetailedSyscallEnter_writev details -> syscallEnterToFormatted details

DetailedSyscallEnter_read details -> syscallEnterToFormatted details

DetailedSyscallEnter_close details -> syscallEnterToFormatted details
Expand Down Expand Up @@ -3430,6 +3474,8 @@ formatDetailedSyscallExit detailedExit handleUnimplemented =

DetailedSyscallExit_write details -> formatDetails details

DetailedSyscallExit_writev details -> formatDetails details

DetailedSyscallExit_read details -> formatDetails details

DetailedSyscallExit_close details -> formatDetails details
Expand Down
24 changes: 23 additions & 1 deletion src/System/Hatrace/Types.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,19 @@ module System.Hatrace.Types
, RLimitType(..)
, ResourceType(..)
, RLimitStruct(..)
, IovecStruct(..)
) where

import Control.Monad (filterM)
import Data.Bits
import Data.List (intercalate)
import Data.Map (lookup)
import Data.Maybe (catMaybes)
import Data.Void (Void)
import Foreign.C.Types (CShort(..), CUShort(..), CInt(..), CUInt(..), CLong(..), CULong(..))
import Foreign.Marshal (copyBytes)
import Foreign.Marshal.Array (peekArray, pokeArray)
import Foreign.Ptr (plusPtr, castPtr)
import Foreign.Ptr (plusPtr, castPtr, Ptr)
import Foreign.ForeignPtr (withForeignPtr, newForeignPtr_)
import Foreign.Storable (Storable(..))
import qualified System.Posix.Signals as Signals hiding (inSignalSet)
Expand Down Expand Up @@ -1729,3 +1731,23 @@ $(deriveEnumTypeClasses ''RLimitType
, ('ResourceRTPrio, (#const RLIMIT_RTPRIO), "RLIMIT_RTPRIO")
, ('ResourceRTTime, (#const RLIMIT_RTTIME), "RLIMIT_RTTIME")
])

data IovecStruct = IovecStruct
{ iov_base :: Ptr Void -- ^ Starting address
, iov_len :: CULong -- ^ Number of bytes to transfer
} deriving (Eq, Ord, Show)

instance ArgFormatting IovecStruct where
formatArg (IovecStruct {..}) =
StructArg [("iov_base", formatArg iov_base), ("iov_len", formatArg iov_len)]

instance Storable IovecStruct where
sizeOf _ = #{size struct iovec}
alignment _ = #{alignment struct iovec}
peek p = do
iov_base <- #{peek struct iovec, iov_base} p
iov_len <- #{peek struct iovec, iov_len} p
return IovecStruct{..}
poke p IovecStruct{..} = do
#{poke struct iovec, iov_base} p iov_base
#{poke struct iovec, iov_len} p iov_len
38 changes: 37 additions & 1 deletion test/HatraceSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Control.Monad.IO.Class (liftIO)
import Control.Monad.IO.Unlift (MonadUnliftIO)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8
import Data.Conduit
import qualified Data.Conduit.Combinators as CC
import qualified Data.Conduit.List as CL
Expand All @@ -24,7 +25,7 @@ import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Foreign.C.Error (Errno(..), eBADF, eCONNRESET)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (nullPtr, plusPtr)
import Foreign.Ptr (nullPtr, plusPtr, ptrToWordPtr)
import Foreign.Storable (sizeOf, peek, poke)
import System.FilePath (takeFileName, takeDirectory)
import System.Directory (doesFileExist, removeFile)
Expand Down Expand Up @@ -116,6 +117,41 @@ spec = before_ assertNoChildren $ do
exitCode `shouldSatisfy` \x ->
x `elem` [ExitFailure 11, ExitFailure (128+11)]

describe "writev" $ do
it "cant peek correct values of struct iov and number of byte written" $ do
callProcess "make" ["--quiet", "example-programs-build/writev"]

argv <- procToArgv "example-programs-build/writev" []
(exitCode, events) <-
sourceTraceForkExecvFullPathWithSink argv $
syscallExitDetailsOnlyConduit .| CL.consume
exitCode `shouldBe` ExitSuccess

let [(stdinReads, (iov1:iov2:_), writtenCount_)] =
[ (bufContents, iovs, writtenCount)
| (_pid
, Right (
DetailedSyscallExit_writev
SyscallExitDetails_writev
{ enterDetail = SyscallEnterDetails_writev{ fd = 1, bufContents, iovs }
, writtenCount
}
)
) <- events
]
-- The example program prints out some expected values, we retrieve them relying on the write syscall
let (expectedCount:expectedVect1:expectedVect2:_) = [ bufContents |
(_pid, Right (
DetailedSyscallExit_write
SyscallExitDetails_write{ enterDetail = SyscallEnterDetails_write { bufContents } }
)
) <- events
]
stdinReads `shouldBe` ["hello ", "world\n"]
fromIntegral writtenCount_ `shouldBe` (read (B8.unpack expectedCount) :: Int)
fromIntegral (ptrToWordPtr $ iov_base iov1) `shouldBe` (read (B8.unpack expectedVect1) :: Int)
fromIntegral (ptrToWordPtr $ iov_base iov2) `shouldBe` (read (B8.unpack expectedVect2) :: Int)

describe "sourceRawTraceForkExecvFullPathWithSink" $ do

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

0 comments on commit c8d1346

Please sign in to comment.